洛谷p1217做題記錄
P1217做題記錄
原本只是想做一道簡單的小題來放鬆一下,但是後來不知怎麼卻被卡了兩天,感覺實在過意不去但是還是來記錄一下吧
題目讓求從a到b的所有迴文質數 1=<a<=100000000
題目的提示給出了個三重迴圈
for (d1 = 1; d1 <= 9; d1+=2) { // 只有奇數才會是素數 for (d2 = 0; d2 <= 9; d2++) { for (d3 = 0; d3 <= 9; d3++) { palindrome = 10000*d1 + 1000*d2 +100*d3 + 10*d2 + d1;//(處理迴文數...) } } }
題目最高達到了9位數,所以這道題首先想到的就是用一個數組來儲存各位數字然後用dfs來進行判斷。 最開始時記錄a的位數並且把a的值放入陣列中然後挨個深搜,如果結果大於b就退出。 所以程式碼便出來了
#include #include #include using namespace std; int bgn,en; int fwei=0,ewei=0; int refer[10]; //endwei從1開始列舉 bool output(int value[],int cur,int endwei){ //從最開始列舉到最後一個數位,這樣形成順序結構 //加一個判斷到達bit位就輸出 int bit=endwei%2?endwei/2:endwei/2-1; if(cur>bit) return true; value[0]=value[0]%2?value[0]:value[0]+1; int k=cur?1:2; for(int i=value[cur];i<=9;i+=k){ value[cur]=i; //到達輸出位 if(cur==bit){ //算出值 int result=0; for(int m=0;m<=bit;m++){ if(endwei%2&&m==bit){ result+=value[m]*pow(10,endwei-m-1); }else{ result+=value[m]*pow(10,endwei-m-1)+value[m]*pow(10,m); } } //檢查是不是素數 /*int A[]={2,3,5,7}; bool ok=1; for(int m=0;m<4;m++){ if(result%A[m]==0&&result!=A[m]){ ok=0; break; } } if(!ok){ continue; }*/ if(result%6!=1&&result%6!=5){ continue; } int r=sqrt(result)+1; bool okk=1; for(int i=2;i<r;i++){ if(result%i==0){ okk=0; break; } } if(!okk) continue; //檢查值是不是比限定值大 if(result>en) return false; printf("%d\n",result); }
if(!output(value,cur+1,endwei) ){ return false; }else if(cur<bit){ for(int i=cur+1;i<=bit;i++){ value[i]=0; } } } return true;
}
void judge(int wei,int sta){
int value[6];
int k=0;
//把每一位的數字都取出來然後存入陣列中
for(k=0;k<wei;){
int temp=(float)sta/pow(10,wei-k-1);
temp%=10;
value[k++]=temp;
}
for(int i=k;i<=ewei;i++){output(value,0,i); //每次迴圈完後設置為1000.。。。方便下一次的運算 memset(value,0,sizeof(value)); value[0]=1; }
}
int main(){cin >> bgn >> en; int temp=bgn; while(temp){ fwei++; temp/=10; } temp=en; while(temp){ ewei++; temp/=10; } //printf("開始的位數是%d,結束的位數是%d\n",fwei,ewei); judge(fwei,bgn);
}
但這樣肯定會tle所以就需要進行一個判斷
我們可以發現偶數位的迴文質數前半部分後後半部分總是相同的所以我們可以顯而易見地得到偶數位的迴文質數中奇數位的和總是和偶數位的和相同,所以利用數的整除性理論中的差系 從右至左,奇陣列數字之和-偶陣列數字之和的差若能被某數整除,則該數就能被某數整除。 以五位數abcde為例:abcde= a*10^4+b*10^3+c*10^2+d*10+e=a*(9999+1)+b*(1001-1)+c*(99+1)+d*(11-1)+e= a*9999+b*1001+c*99+d*11+(a-b+c-d+e),劃線部分已為11的整數倍,故只需確定(a+c+e)-(b+c)是否能被11整除即可。
這裡我也是看大佬的知乎回答才知道得這是大佬的知乎回答
所以我們列舉時就可以跳過總數位是偶數的迴文數。
這樣就得到了AC程式碼:
#include
#include #include using namespace std; int bgn,en; int fwei=0,ewei=0; //endwei從1開始列舉 bool output(int value[],int cur,int endwei){ //從最開始列舉到最後一個數位,這樣形成順序結構 //加一個判斷到達bit位就輸出 int bit=endwei%2?endwei/2:endwei/2-1; if(cur>bit) return true; value[0]=value[0]%2?value[0]:value[0]+1; int k=cur?1:2; for(int i=value[cur];i<=9;i+=k){ value[cur]=i; //到達輸出位 if(cur==bit){ //算出值 int result=0; for(int m=0;m<=bit;m++){ if(endwei%2&&m==bit){ result+=value[m]*pow(10,endwei-m-1); }else{ result+=value[m]*pow(10,endwei-m-1)+value[m]*pow(10,m); } } //檢查是不是素數 if(result%6!=1&&result%6!=5){ continue; } int r=sqrt(result)+1; bool okk=1; for(int i=2;i en) return false; printf("%d\n",result); } if(!output(value,cur+1,endwei) ){ return false; }else if(cur<bit){ for(int i=cur+1;i<=bit;i++){ value[i]=0; } } } return true;
}
void judge(int wei,int sta){
int value[6];
int k=0;
//把每一位的數字都取出來然後存入陣列中
for(k=0;k<wei;){
int temp=(float)sta/pow(10,wei-k-1);
temp%=10;
value[k++]=temp;
}
if(k2){
printf("11\n");
k=k+1;
}
if(k%20) {
k+=1;
memset(value,0,sizeof(value));
value[0]=1;
}for(int i=k;i<=ewei;i+=2){ if(i==9) continue; output(value,0,i); //每次迴圈完後設置為1000.。。。方便下一次的運算 memset(value,0,sizeof(value)); value[0]=1; //if(i==1) printf("11\n"); }
}
int main(){
cin >> bgn >> en;
int temp=bgn;
while(temp){
fwei++;
temp/=10;
}
temp=en;
while(temp){
ewei++;
temp/=10;
}
//printf("開始的位數是%d,結束的位數是%d\n",fwei,ewei);
judge(fwei,bgn);}
但此時還有其他方法比如埃式篩選法,簡而言之埃式篩選法就是從2開始將列舉到的數的所有倍數標記為合數,之後繼續列舉做和前面相同的運算。 那麼剩下的數就是素數
int t=sqrt(b);
//開始列舉
for(int i=2;i
還有一種辦法就是線性篩,也就是一個合數一定可以看成一個最小質因數和一個最大質因數的積,那麼只需要挨個列舉最大最小質因數就可以了
這個我也是看洛谷的題解才知道的
int cnt=0;
int value[MAXN]
for(int i=2;i
每次做完題看洛谷的大佬們的題解總感覺自己這個大學生白當了,總之好好努力吧