http://poj.org/problem?id=3074
Sudoku
Description In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example,
Given some of the numbers in the grid, your goal is to determine the remaining numbers such that the numbers 1 through 9 appear exactly once in (1) each of nine 3 × 3 subgrids, (2) each of the nine rows, and (3) each of the nine columns. Input The input test file will contain multiple cases. Each test case consists of a single line containing 81 characters, which represent the 81 squares of the Sudoku grid, given one row at a time. Each character is either a digit (from 1 to 9) or a period (used to indicate an unfilled square). You may assume that each puzzle in the input will have exactly one solution. The end-of-file is denoted by a single line containing the word “end”. Output For each test case, print a line representing the completed Sudoku puzzle. Sample Input .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 Sample Output 527389416819426735436751829375692184194538267268174593643217958951843672782965341 416837529982465371735129468571298643293746185864351297647913852359682714128574936 Source |
数独求解,用一般的搜索要超时,从中间开始搜大概900+ms过。优雅的求解数独(精确覆盖问题)需要用到Dancing Links这个非常高大上的数据结构,《算法竞赛入门经典——训练指南》(刘汝佳)中有对DLX(Dancing Links X算法)的介绍。
DLX模板:
http://blog.youkuaiyun.com/u012965890/article/details/35258589
DLX本身并不复杂,写起来可能有点小麻烦,但是求解这类问题最难的是构造出01矩阵 。
矩阵的行表示决策(我们要选择行),一共有9*9*9个决策:在r行c列的格子里填上数字v;
矩阵的列表示任务(目的是使得每列有且仅有1个1),一共有4种任务:
1.a行b列有数字
2.a行有数字b
3.a列有数字b
4.第a个方块要有数字b
所以一共有9*9*4列;
这样9*9的数独问题就转化成9*9*9行,9*9*4列的精确覆盖问题
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<ctime>
#include<cctype>
#include<cmath>
#include<string>
#include<cstring>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<map>
#include<set>
#define sqr(x) ((x)*(x))
#define LL long long
#define itn int
#define INF 0x3f3f3f3f
#define PI 3.1415926535897932384626
#define eps 1e-10
#define mm
using namespace std;
const int maxc=9*9*4+10;
const int maxnode=9*9*9*4+10;
const int maxr=9*9*9+10;
struct DLX
{
int n,sz;//列数,结点数
int S[maxc];//各列结点数
int row[maxnode],col[maxnode];//各点行列编号
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//十字链表
int ansd,ans[maxr];//解
void init(int n)
{
this->n=n;
//虚拟结点
for (int i=0;i<=n;i++)
{
U[i]=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);
}
//插入决策行 _r 行号 columns 记录结点列号·[1,n]
void add_row(int _r,vector<int> columns)
{
int first=sz;
for (int i=0;i<columns.size();i++)
{
int _c=columns[i];
L[sz]=sz-1;R[sz]=sz+1;
D[sz]=_c;U[sz]=U[_c];//成环
D[U[_c]]=sz;U[_c]=sz;
row[sz]=_r;col[sz]=_c;
S[_c]++;sz++;
}
R[sz-1]=first;L[first]=sz-1;
}
//删除一列结点
void _remove(int _c)
{
L[R[_c]]=L[_c];
R[L[_c]]=R[_c];
for (int i=D[_c];i!=_c;i=D[i])
for (int j=R[i];j!=i;j=R[j])
{
U[D[j]]=U[j];D[U[j]]=D[j];S[col[j]]--;
}
}
//恢复一列结点,和删除顺序相反
void _resume(int _c)
{
for (int i=U[_c];i!=_c;i=U[i])
for (int j=L[i];j!=i;j=L[j])
{
U[D[j]]=j;D[U[j]]=j;S[col[j]]++;
}
L[R[_c]]=_c;
R[L[_c]]=_c;
}
bool dfs(int d)
{
if (R[0]==0)
{
ansd=d; //记录解的长度
return true;
}
//找最少结点的列删除
int _c=R[0];
for (int i=R[0];i!=0;i=R[i])
if (S[i]<S[_c]) _c=i;
_remove(_c);
for (int i=D[_c];i!=_c;i=D[i])
{
ans[d]=row[i];
for (int j=R[i];j!=i;j=R[j])
_remove(col[j]);
if (dfs(d+1)) return true;
for (int j=L[i];j!=i;j=L[j])//反向恢复
_resume(col[j]);
}
_resume(_c);
return false;
}
bool solve(vector<int> &v)
{
v.clear();
if (!dfs(0)) return false;
for (int i=0;i<ansd;i++) v.push_back(ans[i]);
return true;
}
};
int encode(int a,int b,int c)
{
return a*81+b*9+c+1;
}
void decode(int code,int &a,int &b,int &c)
{
code--;
c=code%9; code/=9;
b=code%9; code/=9;
a=code;
}
char sudoku[100];
DLX solver;
int main()
{
#ifndef ONLINE_JUDGE
freopen("/home/fcbruce/文档/code/t","r",stdin);
#endif // ONLINE_JUDGE
while (scanf("%s",sudoku),strcmp(sudoku,"end"))
{
solver.init(9*9*4);
for (int i=0;i<9;i++)
{
for (int j=0;j<9;j++)
{
for (int v=0;v<9;v++)
{
if (sudoku[i*9+j]=='.' || sudoku[i*9+j]=='1'+v)
{
vector<int> columns;
columns.push_back(encode(0,i,j));//i行j列要有数字
columns.push_back(encode(1,i,v));//i行要有数字v
columns.push_back(encode(2,j,v));//j列要有数字v
columns.push_back(encode(3,(i/3)*3+j/3,v));//第(i/3)*3+j/3个方块要有数字v
solver.add_row(encode(i,j,v),columns);
}
}
}
}
vector<int> ans;
solver.solve(ans);
for (int i=0;i<ans.size();i++)
{
int r,c,v;
decode(ans[i],r,c,v);
sudoku[r*9+c]='1'+v;
}
puts(sudoku);
}
return 0;
}