DFS - POJ 2676 - Sudoku

DFS - POJ 2676 - Sudoku

9×9給定一個9×9的方陣進行數獨遊戲。
在這裏插入圖片描述

要求每一行,每一列,每一個小九宮格中均不能有重複數字。

T9×90T組測試用例,每組包括一個9×9的數字方陣,'0'表示空位。

保證有解,輸出結果。

Sample Input

1
103000509
002109400
000704000
300502006
060000050
700803004
000401000
009205800
804000107

Sample Output

143628579
572139468
986754231
391542786
468917352
725863914
237481695
619275843
854396127

Time limit: 2000 ms
Memory limit: 65536 kB


分析:

dfs暴力dfs每一個空位能夠枚舉的數字,找到一組可行解就返回。

剪枝方案:

()①、優化搜索順序:優先搜索分支較少的節點(能夠選擇的數字較少的空位)。

②、可行性剪枝:行、列、小九宮格內不能存在重複數字。

具體落實:

9019rowcol3×3cell①、 用一個長度位9的二進制數表示當前行、當前列、當前位置所在的小九宮格的狀態。\\\qquad'0'表示當前位的數字不能填,'1'表示可填。\\\qquad因此開長度爲9的數組row和col來存儲每一行的狀態,3×3的方陣cell來存儲九個小九宮格的狀態。

ones1log2X1②、數組ones來記錄每種狀態中'1'的個數,也就是可供選擇的數字的個數。\\\qquad同時要預處理數組快速查詢log_2X,來查看具體是哪一位上的'1',也就是填了哪個數字。

draw(x,y)01③、draw函數來進行在位置(x,y)上填寫數字和刪去已填寫的數字的操作。\\\qquad填寫數字就是在對應的行、列、九宮格的狀態所對應的位置填'0'。刪去這個數就是在該位填'1'

get(x,y)④、get函數用來求位置(x,y)能夠填的數的狀態。等於行、列、九宮格狀態相與。

dfs⑤、每次操作我們先查找所有空位中,可供選擇的數字的個數最少的空位的位置。\\\qquad計算這個位置的狀態,接着枚舉這個位置能填的數字,開始dfs。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

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

int ones[M],log_2[M];
int row[N],col[N],cell[3][3];
char str[10][10];

void cal()
{
    for(int i=0;i<M;i++)
        for(int j=0;j<N;j++)
            ones[i]+=(i>>j&1);
    for(int i=0;i<N;i++) log_2[1<<i]=i;
}

void Init()
{
    for(int i=0;i<N;i++)  row[i]=col[i]=(1<<N)-1;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            cell[i][j]=(1<<N)-1;
}

void draw(int x,int y,int t,bool is_set)
{
    if(is_set) str[x][y]='1'+t;
    else str[x][y]='0';
    
    int v=1<<t;
    if(!is_set) v=-v;  //刪除第t位
    
    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)  //計算(x,y)能填哪些數
{
    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][j]=='0')
            {
                int st=get(i,j);
                if(ones[st]<minv)
                {
                    minv=ones[st];
                    x=i,y=j;
                }
            }
    
    int st=get(x,y);
    for(int i=st;i;i-=lowbit(i))
    {
        int t=log_2[lowbit(i)];
        draw(x,y,t,true);
        if(dfs(cnt-1)) return true;
        draw(x,y,t,false);   //回溯
    }
    
    return false;
}

int main()
{
    cal();
    
    int T;
    cin>>T;
    while(T--)
    {
        Init();
        for(int i=0;i<9;i++) cin>>str[i];
        
        int cnt=0;  //需要填的數的個數
        for(int i=0;i<N;i++)     //(i,j)是二維座標,k是一維座標
            for(int j=0;j<N;j++)
                if(str[i][j]!='0')
                {
                    int t=str[i][j]-'1';
                    draw(i,j,t,true);
                }
                else cnt++;
                
        dfs(cnt);
        
        for(int i=0;i<9;i++)
            printf("%s\n",str[i]);
    }
    
    return 0;
}

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