博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【CJOJ1793】【USACO 4.3.2】素数方阵
阅读量:5051 次
发布时间:2019-06-12

本文共 4013 字,大约阅读时间需要 13 分钟。

题面

Description

在下面的方格中,每行,每列,以及两条对角线上的数字可以看作是五位的素数。方格中的行按照从左到右的顺序组成一个素数,而列按照从上到下的顺序。两条对角线也是按照从左到右的顺序来组成。

这里写图片描述

这些素数各个数位上的和必须相等。

左上角的数字是预先定好的。
一个素数可能在方阵中重复多次。
如果不只有一个解,将它们全部输出(按照这25个数字组成的25位数的大小排序)。
一个五位的素数开头不能为0(例如:00003 不是五位素数)

Input

一行包括两个被空格分开的整数:各个位的数字和 和左上角的数字。

Output

对于每一个找到的方案输出5行,每行5个字符, 每行可以转化为一个5位的质数.在两组方案中间输出一个空行. 如果没有解就单独输出一行"NONE"。

Sample Input

11 1

Sample Output

11351

14033
30323
53201
13313

11351

33203
30323
14033
33311

13313

13043
32303
50231
13331

题解

这道题,搜索好题。

先说我的第一种方法:

首先筛选质数(一个很显然的优化:只需要数字和为给定值的质数)

然后是将质数建立字典树
然后从第一列开始,每列填写一个质数,然后每行每对角线依次检查,判断是否可行。每次的解先存起来,最后排序、输出。

长长的代码

#include
#include
#include
#include
#include
#include
#include
using namespace std;bool zs(int x){ int t=sqrt(x); for(int i=2;i<=t;++i) if(x%i==0)return false; return true;}int cnt=0,tot=0,pri[5000];int S,A;int g[6][6];int number[5000][6];struct Node{ int vis[10];//字典树 }t[100000];struct Ans{ int g[6][6];}ans[100];bool operator <(Ans a,Ans b){ for(int i=1;i<=5;++i) for(int j=1;j<=5;++j) if(a.g[i][j]!=b.g[i][j]) return a.g[i][j]
S)return false;//如果和已经大于S if(x==5&&s!=S)return false;//最后的和必须为S //检验左下到右上对角线 s=now=0; for(int j=1;j<=x;++j) { s+=g[6-j][j]; if(t[now].vis[g[6-j][j]]==0)return false; now=t[now].vis[g[6-j][j]]; } if(s>S)return false;//如果和已经大于S if(x==5&&s!=S)return false;//最后的和必须为S for(int i=1;i<=5;++i)//枚举行 { now=s=0; for(int j=1;j<=x;++j) { s+=g[i][j]; if(t[now].vis[g[i][j]]==0)return false; now=t[now].vis[g[i][j]];//字典树 } if(s>S)return false;//如果和已经大于S if(x==5&&s!=S)return false;//最后的和必须为S } return true;}void DFS(int x){ //outp(); //cout<
>S; cin>>A; for(int i=10003;i<=99999;i+=2) Add(i); memset(g,-1,sizeof(g)); for(int i=1;i<=tot;++i)//枚举第一列 { if(pri[i]/10000==A) { g[1][1]=A; g[2][1]=pri[i]/1000%10; g[3][1]=pri[i]/100%10; g[4][1]=pri[i]/10%10; g[5][1]=pri[i]%10; DFS(2);//搜索 } if(pri[i]/10000>A)break; } sort(&ans[1],&ans[sum+1]); for(int k=1;k<=sum;++k) { for(int i=1;i<=5;++i) { for(int j=1;j<=5;++j) printf("%d",ans[k].g[i][j]); printf("\n"); } printf("\n"); } if(sum==0) cout<<"NONE"<

这样子求解还是很容易的,但是,并不能够随时判断一个质数是否能够填上去,导致会求出大量的无用解法,导致超时。
这里写图片描述
那么,证明这样一列一列填还是效率太低。

然后看我的第二种解法:

首先还是找出所有满足条件的质数,
暴力求出他们的前缀和后缀(其实建立字典树也是可以的)
接下来,每次搜索一个格子(从第一行开始,自左向右,自上而下),
枚举填的数,并且判断是否可行(判断每一行是否存在前缀,每一列是否存在前缀,主对角线是否存在前缀,副对角线是否存在后缀),这样就能够减去大量的不合理情况。

#include
#include
#include
#include
#include
#include
#include
using namespace std;inline bool zs(int x){ int t=sqrt(x); for(int i=2;i<=t;++i) if(x%i==0)return false; return true;}int S,A;int g[6][6];int hz[1000001],qz[1000001];int pp[6]={1,10,100,1000,10000,100000};int sum=0;int L[10],R[10];int djx1,djx2;inline void outp(){ for(int i=1;i<=5;++i) { printf("%d\n",L[i]); } printf("\n");}inline void Add(int x)//判断是否可行{ int a[6]={0,x/10000,x/1000%10,x/100%10,x/10%10,x%10}; if(a[1]+a[2]+a[3]+a[4]+a[5]!=S)return; if(!zs(x))return; int i=x; qz[i/10000]=qz[i/1000]=qz[i/100]=qz[i/10]=qz[i]=true;//前缀 hz[i%10000]=hz[i%1000]=hz[i%100]=hz[i%10]=hz[i]=true;//后缀}void DFS(int x,int y){ if(y==6)//换行 { y=1;x+=1; } if(x==6)//输出解 { ++sum; outp(); return; } bool ok1,ok2,ok3,ok4; for(int i=0;i<=9;++i)//枚举当前数字 { ok1=qz[L[x]*10+i];//行的值要在前缀中 ok2=qz[R[y]*10+i];//列的值要在前缀中 ok3=((x!=y)||(x==y&&qz[djx1*10+i]));//对角线的值要在前缀中 ok4=((x+y!=6)||(x+y==6&&hz[djx2+i*pp[x-1]]));//对角线的值要在后缀中 if(!(ok1&&ok2&&ok3&&ok4))continue; L[x]=L[x]*10+i; R[y]=R[y]*10+i; if(x==y) djx1=djx1*10+i; if(x+y==6)djx2=djx2+i*pp[x-1]; DFS(x,y+1); //向下搜索 L[x]/=10; R[y]/=10; if(x==y) djx1/=10; if(x+y==6)djx2-=i*pp[x-1]; }}int main(){ freopen("prime.in","r",stdin); freopen("prime.out","w",stdout); cin>>S; cin>>A; for(int i=10003;i<=99999;i+=2) Add(i); memset(g,-1,sizeof(g)); g[1][1]=A; L[1]=R[1]=djx1=A; DFS(1,2); if(sum==0) cout<<"NONE"<

这种方法能够AC,但是效率很低。

这里写图片描述

AC归为AC

其实这题如果更改搜索顺序和得到更好地优化。

并且很明显的一点就是,如果求出了某 行/列/对角线 上的四个数,最后一个是可以直接计算判断的(效率更高)。【但是我很懒,没有继续优化了】

转载于:https://www.cnblogs.com/cjyyb/p/7197184.html

你可能感兴趣的文章
第三阶段:1.数据分析:5.关键数据-转化率
查看>>
crontab是不认识profile的
查看>>
docker--deepin安装
查看>>
linux安装pip tornado lxml等
查看>>
windows 8 httpclient 联网方式
查看>>
虚拟机显卡分配过高的警告(Insufficient video RAM)
查看>>
JavaScript入门经典红皮书阅读笔记6.13(第二篇)
查看>>
file upload download
查看>>
mysql时间区间的查询
查看>>
游戏引擎剖析
查看>>
elasticsearch更改mapping(不停服务重建索引)
查看>>
作品第三课-----网页简易时钟
查看>>
Java复习:集合框架(一张图)
查看>>
online学习和offline学习
查看>>
win10安装多个mysql实例
查看>>
ASP.NET Core本身已经集成了一个轻量级的IOC容器
查看>>
JavaScript | 事件
查看>>
002 使用Appender扩展logger框架
查看>>
hdu4366 Successor (dfs序+zkw线段树)
查看>>
HDU 2674
查看>>