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;
}



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