数独问题

表示蒟蒻现在竟然连dfs都不会了qwq......(我怎么这么蒻啊)

什么数独问题都好难啊!!做来做去,最后还是参考了题解qwq(在此对题解表示深深的orz)

先放上两道今天要讲的题:

luoguP1784
luoguP1074

其中上面的是经典填补数独问题(表示没有怎么玩过数独并且,没有好好看题啊,开始还以为对角线的和也要相等的来着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的浏览~

原文地址:https://www.cnblogs.com/fengxunling/p/9506732.html