Acwing166. 数独

题目链接:166. 数独 - AcWing题库

标签:dfs

思路:

填每个数时,有三个限制:

1.行约束、2.列约束、3.九宫格约束

每次填数时,先选取一个可选数字最少的位置填,这样可以为其他选择多的位置增加约束,以减少搜索次数。

这里也是用一个二进制数来表示,有多少个数是可以选的,如果第i个位置为1,那么 i 可以被选。

代码:

#include<iostream>
using namespace std;

const int N = 9;

int row[N],col[N],ceil[3][3];//用二进制表示,col[i]的第i位为1表:第i位可以被选
int map[1<<N],ones[1<<N];//map记录i的最低位1位于第几位,ones表示数字i有多少个1(即还有多少数可以被选)
char str[100];

int lowbit(int x)
{
    return x & -x;
}

int get(int x,int y)
{
    return row[x] & col[y] & ceil[x/3][y/3];
}

bool dfs(int cnt)
{
    if(!cnt) return true;
    
    //寻找最少选择的位置
    int minv = 10;
    int x,y;
    
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<N;j++)
        {
            if(str[i*9+j]=='.')
            {
                int t = ones[get(i,j)];
                if(t<minv)
                {
                    minv=t;
                    x=i,y=j;
                }
            }
        }
    }
    
    //遍历这个位置的所有选择,如果成功返回true
    for(int i=get(x,y); i ; i-=lowbit(i))
    {
        int t = map[lowbit(i)];
        
        row[x] -= 1<<t;
        col[y] -= 1<<t;
        ceil[x/3][y/3] -= 1<<t;
        str[x*N+y] = '1'+t;
        
        if(dfs(cnt-1)) return true;
        
        row[x] += 1<<t;
        col[y] += 1<<t;
        ceil[x/3][y/3] += 1<<t;
        str[x*N+y] = '.';
    }
    
    return false;
}

int main()
{
    //初始化常数表
    for(int i=0;i<N;i++) map[1<<i]=i;
    for(int i=0;i<1<<N;i++)
    {
        for(int j=i;j;j-=lowbit(j)) ones[i]++;
    }
    
    while(cin>>str && str[0]!='e')
    {
        //将所有位置都预置为所有数都可选
        for(int i=0;i<N;i++) row[i]= (1<<N) -1;
        for(int i=0;i<N;i++) col[i]= (1<<N) -1;
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                ceil[i][j]=(1<<N) - 1;
        
        //加入输入限制,并记录有多少个位置需要被填充
        int cnt = 0;
        for(int i=0;i<N;i++)
        {
            for(int j=0;j<N;j++)
            {
                if(str[i*N+j] != '.')
                {
                    int t = str[i*N+j]-'1';
                    row[i] -= 1<<t;
                    col[j] -= 1<<t;
                    ceil[i/3][j/3] -= 1<<t;
                }
                else cnt++;
            }
        }
        
        dfs(cnt);
        
        cout<<str<<endl;
    }
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值