题意
现有一个未被填充的 5 × 5 5\times 5 5×5 的方阵,给出左上角的值,以及每行每列和两个对角线的和且需要满足所构成的 5 5 5 位数为质数,问所有的方阵填法是什么(字典序从小到大)?
思路
因为每行每列两个对角线必须是一个质数,所以我们可以先将在 [ 10000 , 99999 ] [10000,99999] [10000,99999] 之间的质数先都筛出来。然后考虑暴力,每个点可能是的数都在 0 0 0 到 9 9 9 之间(最高位不为 0 0 0),那么时间复杂度即为 O ( 9 25 ) O(9^{25}) O(925) 绝对会超时。
剪枝1:性质剪枝 在区间 [ 10000 , 99999 ] [10000,99999] [10000,99999] 之间,所有质数的结尾必定不是偶数且不为 5 5 5,所以我们可以构造出来 l a s t last last 数组,数组中的元素即为末尾数字 1 , 3 , 5 , 7 1,3,5,7 1,3,5,7,当我们枚举末尾数字时就可以只枚举 4 4 4 次了。
剪枝2:可行性剪枝 因为当我们已经枚举了一行一列或一个对角线当中的 4 4 4 个数字时,第 5 5 5 个数字已经可以被算出来了,这样可以减少第五个数字的循环,大大的节省了时间。
剪枝3:顺序剪枝 根据上面所用到的剪枝,我们可以找到一种合适的顺序去尽可能的使枚举次数少。因为末尾的数字只需要枚举四次,所以我们优先枚举末尾的数字,尽可能少的枚举中间数字。
每次由其他 4 4 4 个数字计算出来的第五个数字如果小于 0 0 0 就直接退出(开头小于等于 0 0 0),因为是正序枚举,成单调递增,再枚举绝对也小于 0 0 0。如果大于 9 9 9 就这跳过这次,和之前同理。如果同时构成了两个数字第二次全部跳过,因为之前有另外一个数字,再枚举它也有可能符合条件。如果不在我们提前筛出来的质数表中,就直接跳过。
每次枚举出来一个方阵,就将其存入一个字符串数组当中,全部枚举之后从小到大排序(字典序从小到大),最后直接输出答案。
代码
#include<bits/stdc++.h>
using namespace std;
int pri[1000001],cnt;
bool ispri[1000001];
int f[6][6],last[]={1,3,7,9};
string ans[1000001];
int n,k,tot;
bool check(int a,int b,int c,int d,int e){
return ispri[a*10000+b*1000+c*100+d*10+e];
}
void getprime()
{
memset(ispri,1,sizeof(ispri));
ispri[1]=0;
for(int i=2;i<=100000;i++)
if(ispri[i])
for(int j=i;j<=100000/i;j++)
ispri[i*j]=0;
}
int main()
{
scanf("%d %d",&n,&k);
f[1][1]=k;getprime();
for(int A=0;A<4;A++)
{
f[5][5]=last[A];
for(int B=0;B<=9;B++)
{
f[4][4]=B;
for(int C=0;C<=9;C++)
{
f[3][3]=C;
f[2][2]=n-f[1][1]-f[3][3]-f[4][4]-f[5][5];
if(f[2][2]<0) break;
if(f[2][2]>9) continue;
if(!check(f[1][1],f[2][2],f[3][3],f[4][4],f[5][5]))
continue;
for(int D=0;D<4;D++)
{
f[5][1]=last[D];
for(int E=0;E<4;E++)
{
f[5][2]=last[E];
for(int F=0;F<4;F++)
{
f[5][3]=last[F];
f[5][4]=n-f[5][1]-f[5][2]-f[5][3]-f[5][5];
if(f[5][4]<0) break;
if(f[5][4]>9) continue;
if(!check(f[5][1],f[5][2],f[5][3],f[5][4],f[5][5]))
continue;
for(int G=0;G<4;G++)
{
f[1][5]=last[G];
for(int H=0;H<4;H++)
{
f[2][5]=last[H];
for(int I=0;I<4;I++)
{
f[3][5]=last[I];
f[4][5]=n-f[1][5]-f[2][5]-f[3][5]-f[5][5];
if(f[4][5]<0) break;
if(f[4][5]>9) continue;
if(!check(f[1][5],f[2][5],f[3][5],f[4][5],f[5][5]))
continue;
for(int J=0;J<=9;J++)
{
f[4][2]=J;
f[2][4]=n-f[5][1]-f[4][2]-f[3][3]-f[1][5];
if(f[2][4]<0) break;
if(f[2][4]>9) continue;
if(!check(f[5][1],f[4][2],f[3][3],f[2][4],f[1][5]))
continue;
for(int K=1;K<=9;K++)
{
f[1][4]=K;
f[3][4]=n-f[1][4]-f[2][4]-f[4][4]-f[5][4];
if(f[3][4]<0) break;
if(f[3][4]>9) continue;
if(!check(f[1][4],f[2][4],f[3][4],f[4][4],f[5][4]))
continue;
for(int L=1;L<=9;L++)
{
f[1][3]=L;
f[1][2]=n-f[1][1]-f[1][3]-f[1][4]-f[1][5];
if(f[1][2]<=0) break;
if(f[1][2]>9) continue;
if(!check(f[1][1],f[1][2],f[1][3],f[1][4],f[1][5]))
continue;
f[3][2]=n-f[1][2]-f[2][2]-f[4][2]-f[5][2];
if(f[3][2]<0) continue;
if(f[3][2]>9) continue;
if(!check(f[1][2],f[2][2],f[3][2],f[4][2],f[5][2]))
continue;
for(int M=1;M<=9;M++)
{
f[2][1]=M;
f[2][3]=n-f[2][1]-f[2][2]-f[2][4]-f[2][5];
if(f[2][3]<0) break;
if(f[2][3]>9) continue;
if(!check(f[2][1],f[2][2],f[2][3],f[2][4],f[2][5]))
continue;
f[4][3]=n-f[1][3]-f[2][3]-f[3][3]-f[5][3];
if(f[4][3]<0) continue;
if(f[4][3]>9) continue;
if(!check(f[1][3],f[2][3],f[3][3],f[4][3],f[5][3]))
continue;
f[4][1]=n-f[4][2]-f[4][3]-f[4][4]-f[4][5];
if(f[4][1]<=0) continue;
if(f[4][1]>9) continue;
if(!check(f[4][1],f[4][2],f[4][3],f[4][4],f[4][5]))
continue;
f[3][1]=n-f[1][1]-f[2][1]-f[4][1]-f[5][1];
if(f[3][1]<=0) continue;
if(f[3][1]>9) continue;
if(!check(f[1][1],f[2][1],f[3][1],f[4][1],f[5][1]))
continue;
if(!check(f[3][1],f[3][2],f[3][3],f[3][4],f[3][5]))
continue;
tot++;
for(int i=1;i<=5;i++)
for(int j=1;j<=5;j++)
ans[tot]+=f[i][j]+48;
}
}
}
}
}
}
}
}
}
}
}
}
}
if(!tot){puts("NONE");return 0;}
sort(ans+1,ans+tot+1);
for(int i=1;i<=tot;i++)
{
for(int j=0;j<25;j++)
{
printf("%c",ans[i][j]);
if(!((j+1)%5)) puts("");
}
puts("");
}
return 0;
}