hdu 1426 Sudoku Killer ( Dancing Link 精確覆蓋 ),hdusudoku
利用 Dancing Link 來解數獨
具體的可以看 lrj 的訓練指南 和 《 Dancing Links 在搜尋中的應用 》這篇論文
Dancing Link 來求解數獨 , 是通過求解精確覆蓋
精確覆蓋就是給出一個 01 矩陣 , 要求我們選擇一些行 , 使得每一列有且僅有一個 1
對於數獨問題 , 行就是我們的選擇 , 即在第 i 行 第 j 列 放上 數字 k , 所以我們最多有 i * j * k 中選擇
如果某些位置( x , y )已經放了數字 a , 那麼我們的選擇只能是 ( x , y , a ) , 否則的話, 我們可以選擇 ( x , y , 1 ~ 9 )
對於列 , 也就是我們需要滿足的條件 , 這裡有 4 大類條件:
1. 第 x 行 第 y 列要有 數字
2. 第 x 行 要有 數字 y
3. 第 x 列 要有 數字 y
4. 第 x 個宮要有數字 y
所以總共有 9*9*4 列 ( 假設 9*9*4 的數獨的話 )
然後我們只要進行DLX就行了
#include <stdio.h>#include <string.h>#include <algorithm>#include <vector>using namespace std;const int maxn = 9 * 9 * 4 + 10 ;const int maxr = 9 * 9 * 9 + 10 ;const int maxnode = maxr * 4 + maxr + 10 ;#define FOR( i , A , s ) for( int i = A[s] ; i != s ; i = A[i] ) struct DLX{ // maxn 列數 , maxnode 總節點數 , maxr 行數 int n , sz ; int S[maxn] ; int row[maxnode] , col[maxnode] ; int L[maxnode] , R[maxnode] , U[maxnode] , D[maxnode] ; int H[maxr] ; int ansd , ans[maxr] ; void init( int N ) { n = N ; // 第一行的虛擬結點 for( int i = 0 ; i <= n ; i ++ ) { U[i] = D[i] = i ; L[i] = i - 1 ; R[i] = i + 1 ; } R[n] = 0 ; L[0] = n ; sz = n + 1 ;// 每一列的個數 memset( S , 0 , sizeof(S) ) ;// H[i] = -1 表示這一行還沒有 1 // 否則表示第一個 1 的 sz 是多少 memset( H , -1 , sizeof(H)) ; } // 在第r行第c列添加一個1 void Link( int r , int c ) { row[sz] = r ; col[sz] = c ; S[c] ++ ; D[sz] = c ; U[sz] = U[c] ; D[U[c]] = sz ; U[c] = sz ; if( H[r] < 0 ) { H[r] = L[sz] = R[sz] = sz ; } else{ R[sz] = H[r] ; L[sz] = L[H[r]] ; R[L[sz]] = sz ; L[R[sz]] = sz ; } sz ++ ; } // 刪除 第 c 列 void remove ( int c ) { // 刪除虛擬結點中的 c 列 L[R[c]] = L[c] ; R[L[c]] = R[c] ; // 從 c 列向下遍曆 FOR( i , D , c ) { // 刪除遍曆到的行 FOR( j , R , i ) { D[U[j]] = D[j] ; U[D[j]] = U[j] ; -- S[col[j]] ; } } } // 恢複第 c 列 void restore( int c ) { FOR( i , U , c ) { FOR( j , L , i ) { ++S[col[j]] ; U[D[j]] = D[U[j]] = j ; } } L[R[c]] = R[L[c]] = c ; } bool dfs( int d ) {// 如果已經沒有列了 , 演算法結束 if( R[0] == 0 ) { ansd = d ; return true ; } // 找到 s 最小的列 , 加快搜尋的速度 int c = R[0] ; FOR( i , R , 0 ) { if( S[i] < S[c] ) c = i ; } // 刪除第 c 列 remove( c ) ;// 遍曆選中列中有1的行 FOR( i , D , c ) { ans[d] = row[i] ;// 刪除選中行中有1的列 FOR( j , R , i ) { remove( col[j] ) ; } if( dfs( d + 1 ) ) return true ;// 回複刪除掉的列 FOR( j , L , i ) { restore( col[j] ) ; } } restore( c ) ; return false ; } bool solve() { if( !dfs(0) ) return false ; return true ; }} dlx ;int ans[15][15] ;const int SLOT = 0 ;const int ROW = 1 ;const int COL = 2 ;const int SUB = 3 ;inline int encode ( int a , int b , int c ) { return a * 9 * 9 + b * 9 + c + 1 ;}int main(){ char str[5] ; int casn = 0 ; while( scanf( "%s" , str ) != EOF ) { if( str[0] == '?' ) { ans[0][0] = 0 ; }else{ ans[0][0] = str[0] - '0' ; } for( int i = 0 ; i < 9 ; i ++ ) { for( int j = 0 ; j < 9 ; j ++ ) { if( i == 0 && j == 0 ) continue ; scanf( "%s" , str ) ; if( str[0] == '?' ) ans[i][j] = 0 ; else ans[i][j] = str[0] - '0' ; } } if( casn ++ ) { puts( "" ) ; } dlx.init( 9 * 9 * 4 ) ; for( int i = 0 ; i < 9 ; i ++ ) { for( int j = 0 ; j < 9 ; j ++ ) { for( int k = 0 ; k < 9 ; k ++ ) { int rr = encode( i , j , k ) ; if( ans[i][j] == 0 || ans[i][j] == k + 1 ) { dlx.Link( rr , encode( SLOT , i , j ) ) ; dlx.Link( rr , encode( ROW , i , k ) ) ; dlx.Link( rr , encode( COL , j , k ) ) ; dlx.Link( rr , encode( SUB , ( i / 3 ) * 3 + j / 3 , k ) ) ; } } } } dlx.solve() ; for( int i = 0 ; i < dlx.ansd ; i ++ ) { int r , c , v ; int t = dlx.ans[i] ; t -- ; v = t % 9 ; t /= 9 ; c = t % 9 ; t /= 9 ; r = t ; ans[r][c] = v + 1 ; } for( int i = 0 ; i < 9 ; i ++ ) { for( int j = 0 ; j < 9 ; j ++ ) { printf( j == 8 ?"%d\n" : "%d " , ans[i][j] ) ; } } } return 0 ;}