闲谈:和数独规则差不多--然后可以直接按数独1的方法进行就好了..数独1没写题解吧...这里补下QAQ.
1.首先观察计数,我们观察层数会发现,假如我们下标从0开始,那么在我们填一个数(x,y)时,付出的代价是min(min(8-x,x),min(8-y,y))+6;那么假设我们填的数是val,那么得分就是val*min(min(8-x,x),min(8-y,y))+6;
2.和数独1一样,我们可以预先处理出两个数组,一个ones,一个mp.第一个判断二进制数中有几个1,第二个用来判断一个二进制数是2的几次方.为什么要这两个预处理呢?第一个ones我们是用来,在对于每次选择分支少的进行剪枝的,第二个mp枚举可填位子的时候填哪个数.
3.先记录下有多少空是空的的,我们优先填空中选择最少的空,这样搜索的分支将会更小,然后进行递归dfs即可,用个ans记录答案,一旦搜完就更新ans.
4.由于每行,每列,每个九宫格都只能出现1~9,我们用二进制数存一下每行,每列,每个九宫格使用情况.开始将他们赋值为(1<<9)-1.然后一旦一个数填了t,我们就将这个数-(1<<t).emm这就是整个过程了,下面就是看代码怎么写了QAQ
下面就是代码了:
#include <bits/stdc++.h> using namespace std; const int N=9; int a[N][N],cnt=0,ans=-1,val=0,line[N],row[N],cell[N][N]; int ones[1<<9],mp[1<<9]; inline int get(int x,int y,int z)//填哪个数,然后算出来的价值 { return z*(min(min(8-x,x),min(8-y,y))+6); } inline int lowbit(int x)//在可选集合,一个一个数的填. { return x&(-x); } void draw(int x,int y,int t)//在x,y中填t. { a[x][y]=t; row[x]-=(1<<(t-1)); line[y]-=(1<<(t-1)); cell[x/3][y/3]-=(1<<(t-1)); } inline int f(int x,int y) { return cell[x/3][y/3]&row[x]&line[y];//算出可以填的位子 } void dfs(int cnt,int val)//dfs填了所有数,然后更新ans { if(cnt==0) {if(val>ans) ans=val;return;}//递归出口. int vv=10;//先赋值最大可以填的. int x,y;//记录坐标. for(int i=0;i<N;i++) { for(int j=0;j<N;j++) { if(ones[f(i,j)]<vv&&!a[i][j]) { vv=ones[f(i,j)]; x=i,y=j; } } }//找到分支最小的点. //cout<<x<<' '<<y<<' '<<endl; for(int i=f(x,y);i;i-=(lowbit(i))) { int t=mp[lowbit(i)]+1; draw(x,y,t); dfs(cnt-1,val+get(x,y,t)); row[x]+=(1<<(t-1)); line[y]+=(1<<(t-1)); cell[x/3][y/3]+=(1<<(t-1)); a[x][y]=0; } } void init() { for(int i=0;i<N;i++) row[i]=line[i]=(1<<9)-1; for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { cell[i][j]=(1<<9)-1; } } for(int i=0;i<N;i++) mp[1<<i]=i; for(int i=0;i<(1<<N);i++) { for(int j=i;j;j-=lowbit(j)) ones[i]++; } } int main() { init(); for(int i=0;i<N;i++) { for(int j=0;j<N;j++) { cin>>a[i][j]; if(a[i][j]) { draw(i,j,a[i][j]); val+=get(i,j,a[i][j]); } else cnt++; } } dfs(cnt,val);//n个点需要填,现在当前的价值是val了. cout<<ans<<endl; return 0; }
感谢辛格大佬QAQ,又出bug了