數獨(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;
}