中国矿业大学——算法作业3——数独

题目描述

数独 是一种传统益智游戏,你需要把一个 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

#include <iostream>
#include <cstring>

using namespace std;

const int N = 9,M = 1 << N;

int ones[M],map[M];
//ones:记录一个状态值中的1的个数,例:100010000,1的个数为2
//map:记录㏒n为多少(以2为底),例:㏒(2^3) = ㏒8 = 3;map[㏒8] = 3
int row[N],col[N],cel[N / 3][N / 3];

char str[100];

void init()//初始化row[]和col[]数组
{
    for(int i = 0;i < N;i++) row[i] = col[i] = (1 << N) - 1;//初始时全都是未选状态,所以应该是111111111(9个1)
                                                            //2^9 - 1(十进制) = 111111111(二进制)
    for(int i = 0;i < 3;i++)
        for(int j = 0;j < 3;j++)
            cel[i][j] = (1 << N) - 1;
}

int lowbit(int x)//返回二进制中的最后一个"1",返回的形式是2的几次幂
{
    return x & -x;
}

void draw(int x,int y,int t,bool is_set)//在(x,y)根据is_set不同进行填充或删除t
{
    //对于str的操作,用来输入时进行初始化
    if(is_set) str[x * N + y] = '1' + t;//t的值是0-8,加上'1'变成0-9,如果is_set为true,进行添加操作
    else str[x * N + y] = '.';//否则该位置变为'.',意味着空格。

    //对于row,col,cel的操作
    int v = 1 << t;
    if(!is_set) v = -v;

    /*
        由于空格为1,已填充的是0
        所以填充空格时,1->0,所以要减去v
        将一个位置上的数删除变为空格时,0->1,v取负
        举个5位数的例子:
            (1)10100(01234)
            填充2这个位置,把1变成0,则要减去2的2次方为100
                10100
               -  100
               -------
                10000
            (2)10000(01234)
            将2这个位置删除,把0变成1,先把v = (1 << 2) 取反为:1000 0100
                0001 0000
               -1000 0100
               -----------
                0001 0100
    */
    row[x] -= v;
    col[y] -= v;
    cel[x / 3][y / 3] -= v;
}

int get(int x,int y)
{
    return row[x] & col[y] & cel[x / 3][y / 3];//取三个状态值的交集(按位与)
}

bool dfs(int cnt)
{
    if(!cnt) return true;;

    //优化搜索顺序
    int x,y;
    int minv = 10;
    for(int i = 0;i < N;i++)
    {
        for(int j = 0;j < N;j++)
        {
            if(str[i * N + j] == '.')
            {
                int state = get(i,j);//枚举所有合法状态
                if(ones[state] < minv)//寻找状态中空位最少的状态
                {
                    minv = ones[state];
                    x = i;//记录空位最少的状态的下标
                    y = j;
                }
            }
        }
    }

    int state = get(x,y);
    for(int i = state;i;i -= lowbit(i))//枚举空位
    {
        int t = map[lowbit(i)];//t为这个空位应填什么值

        draw(x,y,t,true);
        if(dfs(cnt - 1)) return true;
        draw(x,y,t,false);
    }

    return false;
}
int main()
{
    for(int i = 0;i < N;i++) map[1 << i] = i;//map初始化;例:map[㏒8] = 3

    //处理ones数组,表示所有可能的状态值中,每个状态值中1的个数
    for(int i = 0;i < 1 << N;i++)//0-511种状态值
    {
        for(int j = 0;j < N;j++)//每种状态值有9位
        {
            ones[i] += (i >> j & 1);//每一位中如果为1,则计数
        }
    }

    while(cin>>str,str[0] != 'e')//以end结尾
    {
        init();//初始化row,col,cel

        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';//(0-9)->(0-8)
                    draw(i,j,t,true);
                }
                else cnt++;
            }
        }

        dfs(cnt);

        puts(str);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值