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
每次做完题看洛谷的大佬们的题解总感觉自己这个大学生白当了,总之好好努力吧
</p>