poj3074-Sodoku

解数独。

分析

考虑如何把数独解合法的条件转化为经典的01精确覆盖:

  • 每个格子只能填一个数,1-9
  • 每一列刚好填了1-9
  • 每一行刚好填了1-9
  • 每个九宫格刚好填了1-9

也就是说,每个格子,列,行,九宫格都需要被一个数覆盖,且不能重复覆盖。

精确覆盖的一个很巧妙的,也很常用的建矩阵方法,是把条件拆开,把每一个填入也拆成对四种条件的贡献。

也就是说,我们建一个729*324的矩阵。所有的行表示在\((x,y)\)填入\(k\),前81列表示每个格子被覆盖,后面的各81列分别表示每一列,一行,九宫格被覆盖,那么这就是一个精确覆盖问题了——每次填入一个数,可以对应地在四个规则中产生贡献。

如果已经给到有数了,那么就\((x,y)\)就只能填入规定的那个\(k\),否则可以填1-9。

代码

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=10*10*10;
const int maxm=10*10*5;
const int maxl=10*10;
const int maxp=maxn*maxm;
bool a[maxn][maxm];
char s[maxl];
int ans[10][10];
struct node {
	int l,r,u,d,row,col;
};
int id(int x,int y) {
	return (x-1)*9+y;
}
int hang(int x,int k) {
	return 81+(x-1)*9+k;
}
int lie(int y,int k) {
	return 162+(y-1)*9+k;
}
int bel(int x,int y) {
	return ((x-1)/3)*3+(y-1)/3+1;
}
int gong(int x,int y,int k) {
	return 243+(bel(x,y)-1)*9+k;
}
int choose(int x,int y,int k) {
	return (id(x,y)-1)*9+k;
}
void ANS(int row) {
	int k=(row-1)%9+1;
	row-=k;
	int d=row/9+1;
	int y=(d-1)%9+1;
	int x=(d-y)/9+1;
	ans[x][y]=k;
}
struct DLX {
	node p[maxp];
	int tot,last[maxm],size[maxm];
	void clear(int n) {
		tot=n;
		memset(p,0,sizeof p),memset(last,0,sizeof last),memset(size,0,sizeof size);
		p[0]=(node){n,1,0,0,0,0};
		for (int i=1;i<=n;++i) p[i]=(node){i-1,i+1,i,i,0,i},last[i]=i;
		p[n].r=0;
	}
	void build(int row,int c[],int len) {
		if (!len) return;
		p[++tot]=(node){tot,tot,last[c[1]],p[last[c[1]]].d,row,c[1]};
		p[p[tot].u].d=p[p[tot].d].u=last[c[1]]=tot;
		++size[p[tot].col];
		for (int i=2;i<=len;++i) {
			int x=c[i];
			p[++tot]=(node){tot-1,p[tot-1].r,last[x],p[last[x]].d,row,x};
			p[p[tot].l].r=p[p[tot].r].l=p[p[tot].d].u=p[p[tot].u].d=last[x]=tot;
			++size[p[tot].col];
		}
	}
	void del(int c) {
		p[p[c].l].r=p[c].r,p[p[c].r].l=p[c].l;
		for (int i=p[c].d;i!=c;i=p[i].d) 
			for (int j=p[i].r;j!=i;j=p[j].r) 
				p[p[j].u].d=p[j].d,p[p[j].d].u=p[j].u,--size[p[j].col];
	}
	void back(int c) {
		p[p[c].l].r=p[p[c].r].l=c;
		for (int i=p[c].u;i!=c;i=p[i].u) 
			for (int j=p[i].l;j!=i;j=p[j].l) 
				p[p[j].u].d=p[p[j].d].u=j,++size[p[j].col];
	}
	bool dance() {
		if (!p[0].r) return true;
		int first=p[0].r;
		for (int i=p[0].r;i;i=p[i].r) if (size[i]<size[first]) first=i;
		if (p[first].d==first) return false;
		del(first);
		for (int i=p[first].d;i!=first;i=p[i].d) {
			for (int j=p[i].r;j!=i;j=p[j].r) del(p[j].col);
			ANS(p[i].row);
			if (dance()) return true;
			for (int j=p[i].l;j!=i;j=p[j].l) back(p[j].col);
		}
		back(first);
		return false;
	}
} dlx;
int main() {
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
#endif
	while (~scanf("%s",s+1)) {
		dlx.clear(324);
		memset(a,0,sizeof a),memset(ans,0,sizeof ans);
		if (s[1]=='e') break;
		for (int i=1,k=0;i<=9;++i) for (int j=1;j<=9;++j) {
			int x=(s[++k]=='.'?0:s[k]-'0'),st=(x?x:1),ed=(x?x:9);
			ans[i][j]=x;
			for (int k=st;k<=ed;++k) {
				int cho=choose(i,j,k);
				a[cho][id(i,j)]=a[cho][hang(i,k)]=a[cho][lie(j,k)]=a[cho][gong(i,j,k)]=true;
			}
		}
		for (int i=1;i<=729;++i) {
			static int c[maxm];
			int tot=0;
			for (int j=1;j<=324;++j) if (a[i][j]) c[++tot]=j;
			dlx.build(i,c,tot);
		}
		dlx.dance();
		for (int i=1;i<=9;++i) for (int j=1;j<=9;++j) printf("%d",ans[i][j]);
		puts("");
	}
	return 0;
}
posted @ 2017-04-20 21:57  permui  阅读(483)  评论(0编辑  收藏  举报