poj 3076 Sudoku

题意:

给出一个16*16的数独,要求你将其补全;

数据保证给出的数独有且仅有一个解;


题解:

数独毕竟是数独,并不像今年NOIP考的那个幻方那样有构造方案;

显然这种看上去就很难的问题我们只能靠人类智慧或者用力暴搜;

朴素的搜索就是每一个格子试探性的填入每一个数,然后递归进行直到找到一组解或者不能按照规律进行时停止;

而为了解决数独问题,我们可以将其转化成精准覆盖问题来解决;

转化方法就是将每个限制作为一个列,每个决策作为一个行,并且预处理每个决策能覆盖哪些限制条件;

具体一些,每个决策就是第i行第j列的格子放k这个数,共有16*16*16种;

而限制是:

1.每个格子只能放一个数 (16*16)

2.每一行只能放1-16这些数(16*16)

3.每一列只能放1-16这些数(16*16)

4.每个小数独只能放1-16这些数(16*16)

共有16*16*4个限制;

其中不太容易理解的是第一个,一开始我也认为后三个限制已经包括了第一个了;

但是实际上不是,因为如果把一组1-16扔到一个格子里,然后每个小矩阵放一组,每行每列放一组,很容易构造出不合法却满足后三个限制的数独;

现在建好了覆盖的矩阵,然后就是跑DLX算法了;

然而裸跑DLX很容易陷入一些很深的搜索树中难拔,所以还要限制一下搜索的大概方向;

通过人类智慧我们可以知道,填数独都要往密的地方填!那么每次在矩阵中找到一个决策数最小的列,从那个向下搜就可以了;


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define K 4
#define New (&buf[++tot])
using namespace std;
const int LEN=K*K;
const int M=LEN*LEN;
const int MEM=6*LEN*LEN*LEN;
struct node
{
	node *l,*r,*u,*d;
	int belong,x,y,z;
}buf[MEM],*head,*c[M<<2],*r[LEN][LEN][LEN];
char str[LEN+5];
int ans[LEN][LEN],tot;
void Link(node *&t,int now)
{
	node *x=New;
	if(t==NULL)
		x->l=x->r=x;
	else
		x->l=t,x->r=t->r,
		t->r->l=x,t->r=x;
	x->d=c[now],x->u=c[now]->u;
	c[now]->u->d=x,c[now]->u=x;
	x->belong=now;
	t=x;
}
void Build()
{
	tot=0;
	head=New;
	head->l=head->r=head->u=head->d=head;
	for(int i=0;i<M<<2;i++)
	{
		c[i]=New;
		c[i]->u=c[i]->d=c[i];
		c[i]->belong=i;
	}
	for(int i=0;i<M<<2;i++)
	{
		if(i!=0)
			c[i]->l=c[i-1];
		else
			head->r=c[i],c[i]->l=head;
		if(i!=(M<<2)-1)
			c[i]->r=c[i+1];
		else
			head->l=c[i],c[i]->r=head;
	}
	for(int i=0;i<LEN;i++)
	{
		for(int j=0;j<LEN;j++)
		{
			for(int k=0;k<LEN;k++)
			{
				node *t=NULL;
				int now=i*LEN+j;
				Link(t,now);
				t->x=i,t->y=j,t->z=k;
				now=i*LEN+k+M;
				Link(t,now);
				t->x=i,t->y=j,t->z=k;
				now=j*LEN+k+M+M;
				Link(t,now);
				t->x=i,t->y=j,t->z=k;
				now=(i/K*K+j/K)*LEN+k+M+M+M;
				Link(t,now);
				t->x=i,t->y=j,t->z=k;
				r[i][j][k]=t;
			}
		}
	}
}
void remove(node *x)
{
	x->l->r=x->r;
	x->r->l=x->l;
	for(node *i=x->d;i!=x;i=i->d)
	{
		for(node *j=i->r;j!=i;j=j->r)
		{
			j->u->d=j->d;
			j->d->u=j->u;
		}
	}
}
void resume(node *x)
{
	x->l->r=x;
	x->r->l=x;
	for(node *i=x->d;i!=x;i=i->d)
	{
		for(node *j=i->r;j!=i;j=j->r)
		{
			j->u->d=j;
			j->d->u=j;
//			size[j->belong]++;
		}
	}
}
void slove(node *now)
{
	ans[now->x][now->y]=now->z;
	remove(c[now->belong]);
	for(node *i=now->r;i!=now;i=i->r)
	{
		remove(c[i->belong]);
	}
}
void solve(node *now)
{
	ans[now->x][now->y]=-1;
	resume(c[now->belong]);
	for(node *i=now->r;i!=now;i=i->r)
	{
		resume(c[i->belong]);
	}
}
void print()
{
	for(int i=0;i<LEN;i++)
	{
		for(int j=0;j<LEN;j++)
		{
			printf("%c",ans[i][j]+'A');
		}
		puts("");
	}
}
bool dfs()
{
	node *t=NULL;
	int siz=0x3f3f3f3f,now;
	if(head->r==head)
	{
		print();
		return 1;
	}
	for(node *temp=head->r;temp!=head;temp=temp->r)
	{
		now=0;
		for(node *i=temp->d;i!=temp;i=i->d)
		{
			now++;
		}
		if(now<siz)
			t=temp,siz=now;
	}
	for(node *i=t->d;i!=t;i=i->d)
	{
		slove(i);
		if(dfs())
			return 1;
		solve(i);
	}
	return 0;
}
int main()
{
	while(scanf("%s",str)!=EOF)
	{
		Build();
		memset(ans,-1,sizeof(ans));
		for(int i=0;i<LEN;i++)
		{
			if(i)scanf("%s",str);
			for(int j=0;j<LEN;j++)
			{
				if(str[j]!='-')
				{
					slove(r[i][j][str[j]-'A']);
				}
			}
		}
		dfs();
		puts("");
	}
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章