P1074 靶形数独
刚开始想都没想直接爆搜
然后35
然后试了优先找分值大的点,优先填大的数
发现样例2都过不了
放弃了
考虑怎么剪枝
对于一个点,有多种可能的数
如果可能的数少,那么从这个点下去的分支也会比较少
所以预处理一波
把可以填的点按可以填的数的数量排序一遍
然后按排序后的顺序dfs
然后80...
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; int mul[17][17],cnt,ans=-1,sum,mp[17][17];//mul为分值,mp为初始状态 bool hor[17][17],ver[17][17],blk[17][17];//分别存每个行,列和宫可以放的数 struct node { int x,y,num; bool pd[17]; node() {memset(pd,0,sizeof(pd)); x=y=num=0; } }t[107];//存可以放的点 inline bool cmp(const node a,const node b){return a.num<b.num; } //按可能的数的数量从小到大排序 inline void dfs(int x,int y,int now,int stp) { //cout<<x<<" "<<y<<" "<<mul[x][y]<<"!"; if(stp>cnt) { ans=max(ans,now+sum); return; }//如果放满就尝试更新答案 int xx=(x-1)/3,yy=(y-1)/3,z=xx*3+yy+1; for(int k=9;k;k--) { if(hor[x][k]||ver[y][k]||blk[z][k]) continue;//判断重复 hor[x][k]=ver[y][k]=blk[z][k]=1; //更新标记 dfs(t[stp+1].x,t[stp+1].y,now+mul[x][y]*k,stp+1);//向下一层搜索 hor[x][k]=ver[y][k]=blk[z][k]=0; //清除标记 } } int main() { int a; for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) { a=max(abs(5-i),abs(5-j)); mul[i][j]=10-a; }//预处理分值 for(int i=1;i<=9;i++) { for(int j=1;j<=9;j++) { scanf("%d",&a); sum+=mul[i][j]*a; mp[i][j]=a; if(!a) continue; int x=(i-1)/3,y=(j-1)/3; if(hor[i][a]||ver[j][a]||blk[x*3+y+1][a]) { cout<<-1; return 0; }//判断冲突 hor[i][a]=1; ver[j][a]=1; blk[x*3+y+1][a]=1; } }//预处理行,列和宫的情况 for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) { if(mp[i][j]) continue; t[++cnt].x=i; t[cnt].y=j; for(int k=1;k<=9;k++) { int x=(i-1)/3,y=(j-1)/3; if(hor[i][k]||hor[j][k]||blk[x*3+y+1][k]) t[cnt].pd[k]=1; } for(int k=1;k<=9;k++) if(!t[cnt].pd[k]) t[cnt].num++; }//预处理每个点的可以放的数的数量 sort(t+1,t+cnt+1,cmp);//排序 dfs(t[1].x,t[1].y,0,1);//按排序后的顺序dfs cout<<ans; return 0; }
不会了...
还能怎么剪枝啊!
根本想不到了好吧
然后就去看了看题解
发现题解只按每一行的可能的数来剪枝...
然后我就懵逼了..
为什么我考虑行列和宫三种情况就TLE了
题解只考虑行的情况能过...
后来仔细想了想
题解按行dfs,如果同一行前面已经放了,那么后面同一行的可能就会更少
而我的是到处乱放,前面放完后比较不容易排除后面的可能
应该是因为这个吧...
反正这样能过,搜索大家都会
然后要预处理一波
看看就懂了,很简单的
附上一个输出预处理结果的函数:
inline void out() { cout<<"______"<<endl; for(int i=1;i<=9;i++) { for(int j=1;j<=9;j++) cout<<mul[i][j]<<" "; cout<<endl; } cout<<"______"<<endl; for(int i=1;i<=9;i++) { for(int j=1;j<=9;j++) { int x=(i-1)/3,y=(j-1)/3; cout<<x*3+y+1<<" "; } cout<<endl; } cout<<"______"<<endl; for(int i=1;i<=9;i++) { for(int j=1;j<=9;j++) if(hor[i][j]) cout<<j<<" "; cout<<endl; } cout<<"______"<<endl; for(int i=1;i<=9;i++) { for(int j=1;j<=9;j++) if(ver[i][j]) cout<<j<<" "; cout<<endl; } cout<<"______"<<endl; for(int i=1;i<=9;i++) { for(int j=1;j<=9;j++) if(blk[i][j]) cout<<j<<" "; cout<<endl; } cout<<"______"<<endl; }
然后是满分代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; int mul[17][17],cnt,ans=-1,sum,mp[17][17];//mul为分值,mp为初始状态 bool hor[17][17],ver[17][17],blk[17][17];//分别存每个行,列和宫可以放的数 struct node { int x,y,num; bool pd[17]; node() {memset(pd,0,sizeof(pd)); x=y=num=0; } }t[107];//存可以放的点 struct data { int id,num; data() {id=num=0; } }d[17];//存每一行的可能 inline bool cmp(const data a,const data b){return a.num<b.num; }//按每一行的可能排序 inline void dfs(int x,int y,int now,int stp) { if(stp>cnt) { ans=max(ans,now+sum); return; }//如果放满就尝试更新答案 int xx=(x-1)/3,yy=(y-1)/3,z=xx*3+yy+1; for(int k=9;k;k--) { if(hor[x][k]||ver[y][k]||blk[z][k]) continue;//判断重复 hor[x][k]=ver[y][k]=blk[z][k]=1; //更新标记 dfs(t[stp+1].x,t[stp+1].y,now+mul[x][y]*k,stp+1);//向下一层搜索 hor[x][k]=ver[y][k]=blk[z][k]=0; //清除标记 } } int main() { int a; for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) { a=max(abs(5-i),abs(5-j)); mul[i][j]=10-a; }//预处理分值 for(int i=1;i<=9;i++) { d[i].id=i;//存一下行号,排序后就不会丢了 for(int j=1;j<=9;j++) { scanf("%d",&a); sum+=mul[i][j]*a;//先更新一波初始分数 mp[i][j]=a; if(!a) {d[i].num++; continue; }//如果是零就跳过 int x=(i-1)/3,y=(j-1)/3; if(hor[i][a]||ver[j][a]||blk[x*3+y+1][a]) { cout<<-1; return 0; }//判断冲突 hor[i][a]=1; ver[j][a]=1; blk[x*3+y+1][a]=1; //预处理行列宫的情况 } } sort(d+1,d+10,cmp);//按行排一波 for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) if(mp[d[i].id][j]==0) t[++cnt].x=d[i].id,t[cnt].y=j;//把点按前面排的顺序加进来 dfs(t[1].x,t[1].y,0,1);//按顺序搜下去 cout<<ans; return 0; }