zoukankan      html  css  js  c++  java
  • 数独问题

    表示蒟蒻现在竟然连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的浏览~

  • 相关阅读:
    读完此文让你了解各个queue的原理
    借汇编之力窥探String背后的数据结构奥秘
    汇编高手带你玩转字符串,快上车!
    语雀调研
    产品技能一:抽象能力
    我所认知的敏捷开发
    产品经理需要的技能,我有吗?
    孙正义采访:接下来的30年,一切将被重新定义
    5G小白鼠
    goto语句为啥不受待见
  • 原文地址:https://www.cnblogs.com/fengxunling/p/9506732.html
Copyright © 2011-2022 走看看