AcWing 166. 數獨

題目鏈接:點擊這裏

在這裏插入圖片描述

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

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

int ones[M], map[M];
int row[N], col[N], cell[3][3];
char str[100];

// is_set爲true表示在(x,y)位置填上數t,false表示在(x,y)位置刪掉數t
void draw(int x, int y, int t, bool is_set)
{
    if(is_set)  str[x * N + y] = '1' + t;
    else    str[x * N + y] = '.';

    int v = 1 << t;
    if(!is_set) v = -v;

    row[x] -= v;
    col[y] -= v;
    cell[x / 3][y / 3] -= v;
}

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

int get(int x, int y)           // 求當前格子都能填哪些數,即row & col & cell
{
    return row[x] & col[y] & cell[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 * 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))       // 枚舉所有的1
    {
        int t = map[lowbit(i)];
        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 ++ )            	// lowbit返回2的多少次冪,而map記錄對2取對數的結果
        map[1 << i] = i;
    
    for(int i = 0; i < 1 << N; i ++ )       	// ones存儲每個二進制狀態中有多少個1
        for(int j = 0; j < N; j ++ )
            ones[i] += i >> j & 1;

    while(cin >> str, str[0] != 'e')
    {
        /* 初始化 */
        for(int i = 0; i < N; i ++ )        	// 每行、每列初始狀態都爲111111111
            row[i] = col[i] = (1 << N) - 1;

        for(int i = 0; i < 3; i ++ )        	// 每個九宮格初始狀態都爲111111111
            for(int j = 0; j < 3; j ++ )
                cell[i][j] = (1 << N) - 1;

        int cnt = 0;
        for(int i = 0, k = 0; i < N; i ++ )
        {
            for(int j = 0; j < N; j ++, k ++ )
            {
                if(str[k] != '.')           	// 把初始數字填到對應的格中,改變相應row、col、cell的狀態
                {
                    int t = str[k] - '1';
                    draw(i, j, t, true);
                }
                else cnt ++ ;
            }
        }

        dfs(cnt);

        puts(str);
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章