#10024. 「一本通 1.3 练习 3」质数方阵
***思路:***素数筛
找出数位和符合条件的素数
预处理可能成为答案的数
dfs
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//素数筛
//找出数位和符合条件的素数
//预处理可能成为答案的数
//dfs
#define MAXNUM 100006
bool is_prime[MAXNUM];
int prime1[MAXNUM];
int prime[MAXNUM];//用来存储10000-99999的素数
bool qian[MAXNUM],hou[MAXNUM];//前缀和后缀
bool vis[MAXNUM][10];//用来标记是否出现过,防止重复
int hang[10],lie[10];//行和列
vector<int>son[MAXNUM];//用来存储每个前缀拥有的后一个数字的种类
int base[10]={0,100000,10000,1000,100,10,1};//便于用来处理数据
int idx1=0,idx2=0;//两条对角线
int flag=0;//用来标记是否有答案
int sum,n,cnt=0;
void add(int u,int v)//添加前缀的后一个字母的函数
{
if(vis[u][v])return ;
vis[u][v]=true;
son[u].push_back(v);
}
void init(int n)//预处理
{
//处理素数
int p = 0;
for(int i = 0 ; i <= n ; i++) is_prime[i] = true;
is_prime[0]=is_prime[1] = false;
for(int i = 2 ; i <= n ; i++)
{
if(is_prime[i])
{
prime1[p++] = i;
for(int j = i ; j <=n ; j+=i) is_prime[j] = false;
}
}
//得到需要的素数
for(int i=0;i<p;i++)
{
if(prime1[i]<10000||prime1[i]>99999)continue;
int e = 0;
for(int j=2;j<=6;j++)
e +=prime1[i]/base[j]%10;
if(e != sum)continue;
prime[++cnt]=prime1[i];
}
//处理出每个素数的所有的前后缀!!!
for(int i=1;i<=cnt;i++){
for(int j=1;j<=5;j++){
add(prime[i]/base[j],prime[i]/base[j+1]%10);
qian[prime[i]/base[j+1]]=true;
hou[prime[i]%base[j]]=true;
}
}
}
void dfs(int x,int y)
{
if(x==6)//输出答案
{
flag=1;
for(int i=1;i<=5;i++)
printf("%d\n",hang[i]);
printf("\n");
return ;
}
int u=hang[x];
if(son[hang[x]].size()>son[lie[y]].size()) u=lie[y];//尽量判短的,剪枝,有效降低递归层数 ,不加过90%的点
for(int i=0;i<son[u].size();i++)
{
int v=son[u][i];//获得本个后缀的下一个可能数字
hang[x]=hang[x]*10+v;//处理行
lie[y]=lie[y]*10+v;//处理列
if(x==y)//处理左上-右下对角线
idx1=idx1*10+v;
if(x+y==6)//处理左下-右上对角线
idx2=v*base[7-x]+idx2;
if(qian[hang[x]]&&qian[lie[y]]&&qian[idx1]&&(hou[idx2]||(x==1&&y<5)))//判断是否存在
{
if(y==5) dfs(x+1,1);
else dfs(x,y+1);
}
hang[x]/=10;
lie[y]/=10;
if(x==y)
idx1/=10;
if(x+y==6)
idx2%=base[7-x];
}
return ;
}
int main()
{
scanf("%d%d",&sum,&n);
init(100000);
hang[1]=n,lie[1]=n,idx1=n;//(1,1)点提前处理
dfs(1,2);
if(flag==0)
{
printf("NONE\n");
}
return 0;
}