表示蒟蒻现在竟然连dfs都不会了qwq......(我怎么这么蒻啊)
什么数独问题都好难啊!!做来做去,最后还是参考了题解qwq(在此对题解表示深深的orz)
先放上两道今天要讲的题:
其中上面的是经典填补数独问题(表示没有怎么玩过数独并且,没有好好看题啊,开始还以为对角线的和也要相等的来着qwq)
这个还算是比较简单的,但是为了解决数独问题,我们要有一个前置技能:将二维数组(存坐标用的)拆分成行,列,宫(每一个宫就是一个9×9的正方形格子)三个数组来看—— h[row][number],l[column][number],ge[cnt][number] ;
那个number里面存的是1~9这9个数字,来判断其是否出现过。
而这个cnt怎么表示呢,就是((row-1)/3*3+(column-1)/3+1) (不理解的话可以手动模拟一下就知道了)
下面上代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int h[10][10],l[10][10],ge[10][10],a[10][10];
void print()
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
printf("%d ",a[i][j]);
cout<<endl;
}
exit(0);
}
void search(int x,int y)
{
if(a[x][y]!=0)
{
if(x==9&&y==9) print();
if(y==9) search(x+1,1);
else search(x,y+1);
}
if(a[x][y]==0)
{
for(int i=1;i<=9;i++)
{
if(h[x][i]&&l[y][i]&&ge[(x-1)/3*3+(y-1)/3+1][i])
{
a[x][y]=i;
h[x][i]=0;
l[y][i]=0;
ge[(x-1)/3*3+(y-1)/3+1][i]=0;
if(x==9&&y==9) print();
if(y==9) search(x+1,1);
else search(x,y+1);
a[x][y]=0;
h[x][i]=1;
l[y][i]=1;
ge[(x-1)/3*3+(y-1)/3+1][i]=1;
//回溯标程
}
}
}
}
int main()
{
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
h[i][j]=1;
l[i][j]=1;
ge[i][j]=1;
}
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
scanf("%d",&a[i][j]);
if(a[i][j]>0)
{
h[i][a[i][j]]=0;
l[j][a[i][j]]=0;
ge[(i-1)/3*3+(j-1)/3+1][a[i][j]]=0;
}
}
search(1,1);
return 0;
}
接下来就是一点进阶了。。。。。
身为NOIP第4题,靶形数独这个题还是很有难度的。。。吧
这个题不仅可能构造不出来数独,而且还要加权求最大值qwq。。。。。天呀啦搜索剪枝不会啊,我TLE了。。。。
基本思路和上面还是比较相像的,但是因为在下太蒻,还是没有写对啊qwq。。。。所以只能参拜题解了(然鹅dalao不屑于写注释吧qwq)。。。在此附上蒟蒻我的详细解释——
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN=10;
const int score[10][10]=
{{0,0,0,0,0,0,0,0,0,0},
{0,6,6,6,6,6,6,6,6,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,9,10,9,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,6,6,6,6,6,6,6,6}};//这个加权是要写出来的
int row[MAXN][MAXN],col[MAXN][MAXN],area[MAXN][MAXN],sdk[MAXN][MAXN];
//row表示的上文中的h数组,col是l数组,其代表的意思请参照上文。sdk表示的是数独图
int row_cnt[MAXN],col_cnt[MAXN],cnt,ans=-1;
//这个表示的是行和列上的填好的数目,目的是为了找行列上填的最多的先搜索,来达到优化搜索的效果
//ans初始化-1是为了在找不到合理情形(即不存在情况)的时候按题目要求输出-1
inline int id(int i,int j){return (i-1)/3*3+1+(j-1)/3;}
//请参照上道题理解该函数意思
inline int calc(){
int tmp=0;
for(int i=1;i<=9;++i)
for(int j=1;j<=9;++j)
tmp+=score[i][j]*sdk[i][j];
return tmp;
}
//这个因为是找最优情况,所以不是直接输出
void dfs(int r,int c,int cpl){
if(cpl==81){
//注意这里不是r==9&&c==9,具体原因请参照下文
ans=max(ans,calc());
return ;
}
for(int k=1;k<=9;++k){
if(row[r][k]||col[c][k]||area[id(r,c)][k]) continue;
//如果已经填过数字的话就可以continue掉了
row[r][k]=true;
col[c][k]=true;
area[id(r,c)][k]=true;
row_cnt[r]++,col_cnt[c]++;
sdk[r][c]=k;
int tmpr=-1,nxt_r=0,tmpc=-1,nxt_c=0;
//我们要寻找同行同列填数最多的来剪枝,优化我们的搜索
for(int i=1;i<=9;++i)
if(row_cnt[i]>tmpr&&row_cnt[i]<9) //如果==9的话就是这一行都填完了
tmpr=row_cnt[i],nxt_r=i;
for(int j=1;j<=9;++j)
if(col_cnt[j]>tmpc&&(!sdk[nxt_r][j])&&col_cnt[j]<9) //在锁定了nxt_r之后就要找同行能填的了,同理
tmpc=col_cnt[j],nxt_c=j;
dfs(nxt_r,nxt_c,cpl+1);
//搜索
row[r][k]=false;
col[c][k]=false;
area[id(r,c)][k]=false;
row_cnt[r]--,col_cnt[c]--;
sdk[r][c]=0;
//基本的回溯操作
}
}
int main(){
for(int i=1;i<=9;++i){
for(int j=1;j<=9;++j){
scanf("%d",&sdk[i][j]);
if(sdk[i][j]!=0){
row[i][sdk[i][j]]=true;
col[j][sdk[i][j]]=true;
area[id(i,j)][sdk[i][j]]=true;
row_cnt[i]++,col_cnt[j]++;
cnt++;
//详情参照上一题
//cnt记录填的数的数量qwq,方便下面搜索
}
}
}
int tmpr=-1,r,tmpc=-1,c;
for(int i=1;i<=9;++i)
if(row_cnt[i]>tmpr&&row_cnt[i]<9)
tmpr=row_cnt[i],r=i;
for(int j=1;j<=9;++j)
if(col_cnt[j]>tmpc&&(!sdk[r][j]))
tmpc=col_cnt[j],c=j;
//和上面的基本同理
dfs(r,c,cnt);
cout<<ans<<endl;
}
以上。
(重看一遍,感觉自己写的好蒻啊.......)
最后蒟蒻在此谢过各位dalao的浏览~