P1784 数独 题解

博客园同步

原题链接

前置知识:

浅谈 Dancing Links X \text{Dancing Links X} Dancing Links X 算法

注:这次的前置知识如果你不会又不看,代码和思路肯定都看不懂的。

简要题意:

填满一个未完成的数独。

首先数独的规则是:

  1. 每行所填数不得重复,为 1 1 1 ~ 9 9 9 之间。

  2. 每列所填数不得重复,为 1 1 1 ~ 9 9 9 之间。

  3. 每宫所填数不得重复,为 1 1 1 ~ 9 9 9 之间。

首先这是一个数学游戏,但是如果你想在 1 s 1s 1s 之内 AK \text{AK} AK 吊打全场,那么就要学习如何解决数独问题。

下面我们来说说,怎么把它扯到 精确覆盖 上面呢。

决策应该是形如 ( i , j , x ) (i,j,x) (i,j,x) 的三元组,表示 a i , j = x a_{i,j} = x ai,j=x.

i i i 行只能用一个 x x x,需要 9 × 9 = 81 9 \times 9 = 81 9×9=81 列。(每个格子都要开一列,对应 1 1 1 ~ 81 81 81 列)

j j j 列只能用一个 x x x ,再开 9 × 9 = 81 9 \times 9 = 81 9×9=81 列。(每个格子都要开一列,对应 82 82 82 ~ 162 162 162 列)

( i , j ) (i,j) (i,j) 所在的宫只能用一个 x x x,再开 9 × 9 = 81 9 \times 9 = 81 9×9=81 列。(每个格子都要开一列,对应 162 162 162 ~ 243 243 243 列)

( i , j ) (i,j) (i,j) 只能填一个数(千万不要忽略这个!),再开 9 × 9 = 81 9 \times 9 = 81 9×9=81 列。(每个格子都要开一列,对应 244 244 244 ~ 324 324 324 列)

那么多少行呢?显然, 9 3 = 729 9^3 = 729 93=729 行,因为每个行、列、宫都要决策一次。

所以我们就将数独问题转化成一个 729 × 324 729 \times 324 729×324 的矩阵上有 729 × 4 = 3216 729 \times 4 = 3216 729×4=3216 1 1 1精确覆盖问题

然后直接套板子,主要建图即可。

时间复杂度: O ( w y s ) O(wys) O(wys).(很优但难以说明,严格来说是 O ( c n ) O(c^n) O(cn) ,其中 n ≤ 3216 n \leq 3216 n3216 c c c 为极其接近 1 1 1 的常数)

实际得分: 100 p t s 100pts 100pts.

通过时间: 11 m s 11ms 11ms.(看到了吧,精确覆盖比搜索剪枝快十几倍)

// 板子部分不再解释
#pragma GCC optimize(2)
#include
using namespace std;

const int N=1e5+1; 
#define FOR(i,A,x) for(int i=A[x];i!=x;i=A[i])

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n,m,id,fir[N],siz[N];
int L[N],R[N],U[N],D[N];
int col[N],row[N],ans;
int stk[N];

inline void remove(int x) {
//	printf("remove : %d\n",x);
	L[R[x]]=L[x]; R[L[x]]=R[x];
	FOR(i,D,x) FOR(j,R,i) U[D[j]]=U[j],D[U[j]]=D[j],--siz[col[j]];
}

inline void recover(int x) {
//	printf("recover : %d\n",x);
	FOR(i,U,x) FOR(j,L,i) U[D[j]]=D[U[j]]=j,++siz[col[j]];
	L[R[x]]=R[L[x]]=x;
}

inline void build(int x,int y) {
//	printf("build : %d %d\n",x,y);
	n=x,m=y; for(int i=0;i<=y;i++)
		L[i]=i-1,R[i]=i+1,U[i]=D[i]=i;
	L[0]=y,R[y]=0,id=y;
	memset(fir,0,sizeof(fir));
	memset(siz,0,sizeof(siz));	
}

inline void insert(int x,int y) {
//	printf("insert : %d %d\n",x,y);
	col[++id]=y,row[id]=x,++siz[y];
//	U[id]=D[id]=y,U[D[y]]=id,D[y]=id;
	D[id]=D[y],U[D[y]]=id,U[id]=y,D[y]=id;
	if(!fir[x]) fir[x]=L[id]=R[id]=id;
	else R[id]=R[fir[x]],L[R[fir[x]]]=id,L[id]=fir[x],R[fir[x]]=id;
}

int a[1001][1001];

inline bool dance(int dep) {
//	printf("dance : %d\n",dep);
	if(!R[0]) {
		for(int i=1;i<dep;i++) {
			int x=(stk[i]-1)/9/9+1,y=(stk[i]-1)/9%9+1,z=(stk[i]-1)%9+1;
			a[x][y]=z; //算出行 , 列 , 宫 , 存储答案
		} return 1;
	}
	int wz=R[0]; FOR(i,R,0) if(siz[i]<siz[wz]) wz=i;
	remove(wz); FOR(i,D,wz) {
		stk[dep]=row[i]; FOR(j,R,i) remove(col[j]);
		if(dance(dep+1)) return 1;
		FOR(j,L,i) recover(col[j]);
	} recover(wz); return 0;
}

int main(){
	build(729,324);
	for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) {
		a[i][j]=read();
		for(int k=1;k<=9;k++) {
			if(a[i][j]-k && a[i][j]) continue;
			int t=((i-1)*9+(j-1))*9+k;
			insert(t,(i-1)*9+j);
			insert(t,81+(i-1)*9+k);
			insert(t,243+(j-1)*9+k);
			insert(t,162+((i-1)/3*3+(j-1)/3)*9+k); //建图
		}
	} dance(1);
	for(int i=1;i<=9;i++) {
		for(int j=1;j<=9;j++) printf("%d ",a[i][j]);
		puts("");
	} 
	return 0;
}

你可能感兴趣的:(Dancing,Links,搜索)