Sudoku
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 11071 | Accepted: 3969 |
Description
In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example,
. | 2 | 7 | 3 | 8 | . | . | 1 | . |
. | 1 | . | . | . | 6 | 7 | 3 | 5 |
. | . | . | . | . | . | . | 2 | 9 |
3 | . | 5 | 6 | 9 | 2 | . | 8 | . |
. | . | . | . | . | . | . | . | . |
. | 6 | . | 1 | 7 | 4 | 5 | . | 3 |
6 | 4 | . | . | . | . | . | . | . |
9 | 5 | 1 | 8 | . | . | . | 7 | . |
. | 8 | . | . | 6 | 5 | 3 | 4 | . |
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
#include<stdio.h>
#include<string.h>
#include <cmath>
#define INF 0x3f3f3f3f
#define MAXM 740
#define MAXN 330
int S[MAXN],H[MAXM];
int L[MAXM*MAXN],R[MAXM*MAXN],U[MAXM*MAXN],D[MAXM*MAXN],Row[MAXM*MAXN],Col[MAXM*MAXN];
int adj[MAXM][MAXN];
int ans[10][10];
//这道题的ID就是正常的i*m+j,不是ID++构造的,ID++自然是更精简,但失去了矩阵数字的规律
void init(int n,int m) //如果是邻接表的输入方式的话,构造出来的一定是个正方形。
{
for(int i=0;i<=m;i++)
{
S[i]=0;
L[i]=i-1;
R[i]=i+1;
U[i]=i;
D[i]=i; //底下一个col上下连接就就好理解了
}
L[0]=m; //双向循环链表
R[m]=0;
for(int i=0;i<=n;i++)
H[i]=-1;
}
//将第row行第col列第id个结点加入数据结构
void link(int row,int col,int id) //row,col的下标是从1开始的,
{
//存第i个结点的行列值,id表示是第几个结点,最开始的一排也是结点,称作列头结点,U,D,的值也是他们的id,有值的结点是从m+1开始的
Row[id]=row; //再次强调,最开始的一排也是结点,最开始的一排结点左右是0.
Col[id]=col; //存第i个结点的行列值
U[id]=U[col]; //用链表去理解
D[id]=col;
D[U[col]]=id;
U[col]=id;
if(H[row]==-1) //列设H数组是因为R,L存的不是对应的位置。而U,D,是对应ID的,所以不用设H数组。
H[row]=L[id]=R[id]=id;//每一行的开头都有个head的头结点,才发现H的头结点就是元素的ID,而且H是不画在图中的,H只是起一个临时作用。
else
{
L[id]=L[H[row]];
R[L[H[row]]]=id;
R[id]=H[row];
L[H[row]]=id;
}
S[col]++;
}
void exact_remove(int c)
{
L[R[c]]=L[c];
R[L[c]]=R[c];
int i,j;
for(i=D[c];i!=c;i=D[i])
{
for(j=R[i];j!=i;j=R[j])
{
U[D[j]]=U[j];
D[U[j]]=D[j];
S[Col[j]]--;
}
}
}
void exact_resume(int c)
{
int i,j;
for(i=D[c];i!=c;i=D[i])
{
for(j=R[i];j!=i;j=R[j])
{
U[D[j]]=j;
D[U[j]]=j;
S[Col[j]]++;
}
}
L[R[c]]=c;
R[L[c]]=c;
}
int dfs(int d)
{
if(R[0]==0)
return 1;
int c=R[0];
int i,j;
for(i=R[0];i!=0;i=R[i]) //这只是第1~324列的枚举
{
if(S[i]<S[c])
c=i;
}
exact_remove(c);
for(i=D[c];i!=c;i=D[i]) //这里的i是下标
{
int r=(i-1)/324; //i>=325,所以r>=1;但r<=729这个就表示是第几行吧
int key=r%9; // r=9*(t-1)+k; k=r%9;
if(key==0)
key=9;
int num=(r+8)/9; //num>=1
int x=(num+8)/9;
int y=num%9;
if(y==0)
y=9; //利用下标找值的规律。 //利用枚举时建立的规律
ans[x][y]=key; //adj[9*(t - 1) + k][81+(i-1)*9+k]=1;
//adj[9*(t - 1) + k][162+(j-1)*9+k]=1
//r%9==k,一晚上才看出来
//t每加1,行数加9,就是说列每加1,行数加9,这样y=(r+8)/9%9,
//加8是因为这部分是k变化引起的行变化
//在加一层,就是x的变化
for(j=R[i];j!=i;j=R[j])
{
exact_remove(Col[j]);
}
if (dfs(d+1))
return 1;
/*这个顺序很重要,删除和恢复的方向必须相反
开始相同,都是向右的,结果TLE了*/
for (j=L[i];j!=i;j=L[j])
{
exact_resume(Col[j]);
}
}
exact_resume(c);
return 0;
}
//套用模板的前提是,构造出该01矩阵,本题是四种约束条件构造的的01矩阵
int main()
{
int n,m;
n=729; //9*9*9 行数,对应种类数。
m=324; //4*9*9 列数,
char str[85];
int i,j,k;
while(scanf("%s",str)!= EOF)
{
if(strcmp(str,"end")==0)
break;
memset(adj,0,sizeof(adj));
for(i=1;i<=9;i++) //i,j就是i行j列
{
for(j=1;j<=9;j++)
{
int t=9*(i-1)+j; //t刚好是序列号
if(str[t-1]=='.') //因为str下标是从0开始的,所以才要减个1.
{
for(k=1;k<=9;k++)
{
//01矩阵的建立
/*
1. 行数:n*n*n n*n个格子; 每个格子有n种情况 所以有n*n*n种情况
2. 列数:4*n*n种情况 ,正如下面每一行都对应4种情况
3.
*/
//adj[i][j]表示舞蹈链中的i行j列
adj[9*(t - 1) + k][t]=1; //每个格子都需要填一个数,t就是所要填的数,让他变为1就表示t这个数在这种情况可填
adj[9*(t - 1) + k][81+(i-1)*9+k]=1; //每一行1~9这几个数都必须出现一次,表示第i行已经有这个数k
adj[9*(t - 1) + k][162+(j-1)*9+k]=1; //每一列1~9这几个数都必须出现一次,表示第j列已经有这个数k
adj[9*(t - 1) + k][243+(3*((i-1)/3)+(int)ceil(1.0*j/3)-1)*9+k]=1; //每个较小的个格子中1~9这几个数都必须出现一次,
//表示第(3*((i-1)/3)+(int)ceil(1.0*j/3)宫格已经有k这个值
}
//adj[i]这个i,没有特殊意义,只是在列的参数下去凑12345。
}
else //一旦确定了就该种,种类自然就确定了,不用枚举,自然总的总类就会减少,这不是废话吗?
{
k=str[t-1]-'0'; //
adj[9*(t-1)+k][t]=1;
adj[9*(t-1)+k][81+(i-1)*9+k]=1; //
adj[9*(t-1)+k][162+(j-1)*9+k]=1;
adj[9*(t-1)+k][243+(3*((i-1)/3)+(int)ceil(1.0*j/3)-1)*9+k]=1;
}
}
}
init(n,m);
int id;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
if(adj[i][j]==1)
{
id=i*m+j;
link(i,j,id);
}
}
}
dfs(0);
for(i=1;i<=9;i++)
{
for(j=1;j<=9;j++)
printf("%d",ans[i][j]);
}
printf("\n");
}
return 0;
}