数独(dfs+优化+位运算)

链接:https://www.acwing.com/problem/content/168/
来源:《算法竞赛进阶指南》

数独是一种传统益智游戏,你需要把一个 9×9 的数独补充完整,使得图中每行、每列、每个 3×3 的九宫格内数字 
1∼9 均恰好出现一次。

请编写一个程序填写数独。

输入格式
输入包含多组测试用例。

每个测试用例占一行,包含 81 个字符,代表数独的 81 个格内数据(顺序总体由上到下,同行由左到右)。

每个字符都是一个数字(1−9)或一个 .(表示尚未填充)。

您可以假设输入中的每个谜题都只有一个解决方案。

文件结尾处为包含单词 end 的单行,表示输入结束。

输出格式
每个测试用例,输出一行数据,代表填充完全后的数独。

输入样例:
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end
输出样例:
417369825632158947958724316825437169791586432346912758289643571573291684164875293
416837529982465371735129468571298643293746185864351297647913852359682714128574936

思路:
在每个空点的时候,遍历所在行,所在列,所在方块,选出能填的数,依次填入,进入下个点,再还原。
毫无疑问,这样会超时。
优化:
用2进制来表示9个数中那个数没被使用,开两个数组,ones和map,ones记录剩余可选数的个数,map记录可以填的数值。就不用等使用的时候一次次的遍历了。另外就是从可能性最少的空点处填起,这样整体递归的规模会小不少,能节约不少时间。
代码:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 9;
char str[100];
int col[N],row[N],cell[3][3];//存放不同行,列,区块可以填的数的情况。
int ones[1<<N],map[1<<N];
inline int lowbit(int x)//方便对2进制进行操作
{
    return x&-x;
}
void init()//初始化函数
{
    for(int i=0;i<N;i++) col[i]=row[i]=(1<<N)-1;
    for(int i=0;i<3;i++)
    for(int j=0;j<3;j++)
    cell[i][j]=(1<<N)-1;
}
inline int get(int i,int j)//行列块中能使用的数的交集
{
    return col[i]&row[j]&cell[i/3][j/3];
}

bool dfs(int cnt)//cnt为空点数量
{
    if(!cnt) return true;//cnt=0时,退出递归
    int min=10,x,y;//寻找可以选的数最少的空
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    {if(str[9*i+j]=='.')
        {int t=ones[get(i,j)];
            if(t<min)
        {
            x=i;
            y=j;
            min=t;
        }
        }
    }
    for(int k=get(x,y);k;k-=lowbit(k))
    {
        int t=map[lowbit(k)];
        col[x]-=1<<t;
        row[y]-=1<<t;
        cell[x/3][y/3]-=1<<t;
        str[x*9+y]='1'+t;
        if(dfs(cnt-1)) return true;
        col[x]+=1<<t;
        row[y]+=1<<t;
        cell[x/3][y/3]+=1<<t;
        str[x*9+y]='.';
    }
    return false;
    
}
int main()
{
   for(int i=0;i<N;i++) map[1<<i]=i;//初始化map和ones
   for(int i=0;i<1<<N;i++)
   {
       int s=0;
       for(int j=i;j;j-=lowbit(j)) s++;
       ones[i]=s;
   }
   while(cin>>str,str[0]!='e')
   {init();
       int cnt=0;
       for(int i=0,k=0;i<N;i++)
       for(int j=0;j<N;j++,k++)
       {
           if(str[k]!='.')//根据已填入的数,变更行列块的信息
           {
               int t=str[k]-'1';
               col[i]-=1<<t;
               row[j]-=1<<t;
               cell[i/3][j/3]-=1<<t;
           }
           else cnt++;
       }
       dfs(cnt);
       cout<<str<<endl;
   }
    
}

总体来说,这题需要优化,并且优化的方式比较复杂。很巧妙,我自己肯定很难想到。需要把这段代码多敲几次,多多感受优化的思想。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值