一道素數打表的水題,順便記下打表高效方法
阿新 • • 發佈:2018-12-24
codeforces round 388 div2的A題
A. Bachgold Problem
題意問輸入的n最有有多少個素數加起來組成,素數可以重複使用,那這題用貪心,從2開始算起,因為你要求素數最多的話,肯定是越小的個數越多,做完之後,突然發現根本不用打表,n>=2,要麼是奇數,要麼是偶數,偶數就全2,奇數就把一個2換成3就可以了,居然如此簡單。。。。但當時做的時候真的是閃出來素數打表,這就是不用腦地套用習慣思維啊,應該每道題好好地去分析題目特性。
第一次做AC程式碼
#include <iostream>
#include <cstring>
using namespace std;
typedef long long int ll;
const int maxn = 100005;
bool p[maxn];//0 means prime
int primeNum[maxn];
void setPrime()
{
memset(primeNum,0,sizeof(primeNum));
memset(p,0,sizeof(p));
for(ll i=2;i<maxn;++i){
if(!p[i]){
for(ll j=i*i;j<maxn;j+=i)p[j]=1;
}
}
}
int main()
{
setPrime();
int n;
scanf("%d",&n);
int ans = 0;
for(int i=2;i<=n;++i){
if(!p[i] && n-i>=0){
primeNum[i] = n/i;
ans = ans + primeNum[i];
n = n- n/i*i;
}
}
if(n>0){
if(primeNum[n]){
ans++;
primeNum[n]++;
}else {//n==1
primeNum[2]--;
primeNum[3]++;
}
}
printf("%d\n",ans);
for(int i=2;i<maxn;++i){
while(primeNum[i]--)
printf("%d ",i);
}
printf("\n");
return 0;
}
發現奇偶規律之後,從打表變成了線性時間
#include <iostream>
using namespace std;
typedef long long int ll;
const int maxn = 100005;
int main()
{
int n;
scanf("%d",&n);
int ans = n/2;
printf("%d\n",ans);
int two = n/2,three = 0;
if(n%2)two--,three++;
while(two--)printf("2 ");
while(three--)printf("3 ");
printf("\n");
return 0;
}
時間從31ms變成了30ms????難道是printf慢嗎?,有興趣的朋友可以改用putchar試試。
既然說到了素數打表,順便記錄一下網上的接近線性的高效打表方法
#include<iostream>
#include<string.h>
#include<stdio.h>
#define maxn 10000000
bool visit[maxn+1000000];
int prime[maxn],n; //prime的大小大概估計一下再開陣列。大概是(x/lnx)
void getprime()
{
memset(visit, false, sizeof(visit));
int num = 0;
for (int i = 2; i <= n; ++i)
{
if ( !visit[i] ) prime[++num] = i;
for (int j = 1; (j <= num) && (i * prime[j] <= n) ; j++)
{
visit[ i * prime[j] ] = true;
//由 i 和 prime[j] 相乘組成的合數賦為true,注意i不一定為素數,但prime[j] 是素數
if (i % prime[j] == 0) break;
/*此處是重點,避免了很多的重複判斷,比如i=9,現在素數是2,3,5,7,進入二重迴圈,visit[2*9]=1;visit[3*9]=1;這個時候9%3==0,要跳出。因為5*9可以用3*15來代替,如果這個時候計算了,i=15的時候又會被重複計算一次,所以這裡避免了重複大量運算。*/
}
}
}
int main()
{
scanf("%d",&n);
getprime();
return 0;
}