算法实践:数独(2)

数独(2)

描述

在这里插入图片描述

输入

输入包含多组测试用例。

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

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

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

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

输出

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

输入样例

.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end

输出样例

527389416819426735436751829375692184194538267268174593643217958951843672782965341
416837529982465371735129468571298643293746185864351297647913852359682714128574936

难度

极难,深搜

解法

从数独中数字最多,空缺最少的位置开始深搜

用9位二进制数代表1-9行的一行,一列,一个3*3的格子,int初始化位111111111

可以使用lowbit计算整数中1的个数,即空缺的个数

也可以使用lowbit计算整数中最低为的位数,即要修改的个数

代码

//code by Andy
#include<bits/stdc++.h>
using namespace std;
const int N9 = 9; 
const int MaxN=1<<N9;  //! 1<<9为512
string str;  
int row[N9],col[N9],cell[3][3];
int ones[MaxN];  //int t=ones[n] 给定一个数n,计算出有t个1
int LOG2[MaxN];  //int t= LOG2[n] 给定一个数n,计算出最低位的1
//! 二进制准备---------------------------------------------
//打表法计算LOG2
void BuildLOG2(){
    for(int i=0;i<N9;i++) LOG2[1<<i] = i;
}
//取n二进制序列最后末一个1000模式的子串
inline int lowbit(int n){
    return n & -n; //!-n表示n取反+1
}
//计算传入的数有多少个是1
int NumberOf1(int n){
    int res = 0;
    while(n){
        n -=lowbit(n);
        res +=1;
    }
    return res;
}
//打表法构建ones数组,用于统计1最少的i就是需要搜索的位置
void Buildones(){
    for(int i=0;i<MaxN;i++) ones[i]=NumberOf1(i);
}
//! --------------------------------------------
//取交集的运算
inline int get(int x,int y){  //返回row[x],col[x]与cell[x][y]的可用集合
    return row[x] & col[y] & cell[x/3][y/3];  //返回同时为1的二进制位
    // 可以使用lowbit取出每一位,就知道哪一位可尝试了 111(7) & 011(3) = 011(3)
}
//把x,y位置二进制数的第n位取反
inline void flipbits(int x,int y,int n){
    row[x] ^= 1<< n;
    col[y] ^= 1<< n;
    cell[x/3][y/3] ^= 1<< n;
}
void init(){  //把全部数都清成1
    for(int i=0;i<N9;i++) row[i]=MaxN-1;  //初始化为111111111
    for(int i=0;i<N9;i++) col[i]=MaxN-1;  //初始化为111111111
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            cell[i][j] = MaxN-1;  //初始化为111111111
}
void printStr2line(string s){
    cout<<s<<endl;
}
//!========================================================================
bool dfs(int cellsLeft){
    if(cellsLeft==0){
        //输出可行解
        printStr2line(str);
        return true;
    }
    //找出可选方案数最小的格子
    int minv = 10;
    int x,y;
    for(int i=0;i<N9;i++)
        for(int j=0;j<N9;j++)
            if(str[i*9+j]=='.'){
                int t = ones[get(i,j)];  //计算出格子最少的行列
                if(t<minv){
                    minv = t; x = i; y = j; //记录为x,y
                }
            }
    //从该方案[x,y,minV]开始枚举,直到找到cellstosolve-1方案为止
    //? get(x,y)的返回值代表行,列,Cell中可选的二进制值
    for(int sk=get(x,y);sk;sk-=lowbit(sk)){
        int t=LOG2[lowbit(sk)];  //得到1的最低位位置
        //修改状态row,col,cell
        flipbits(x,y,t);
        str[x*9+y] = '1'+t;  //修改str对应位置上的数(0-8)映射到(1-9)
        dfs(cellsLeft-1);
        //恢复状态
        str[x*9+y]='.';
        flipbits(x,y,t);
    }
    return false;
}
//! ========================================================================
int main()
{
    BuildLOG2();
    Buildones();
    while(cin>>str,str[0]!='e'){
        init();
        //i,j是九宫格的行列坐标,k是字符串中的线性坐标
        int cellsLeft = 0;
        for(int i=0,k=0;i<N9;i++)
            for(int j=0;j<N9;j++,k++)
                if(str[k]!='.'){
                    int t=str[k]-'1';  //把1-9映射到 0-8
                    flipbits(i,j,t);  //初始化是1,这里取反设置为0,表示该位已经占用
                }
                else cellsLeft++;
        dfs(cellsLeft);   
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值