您的位置:首页 > 其它

poj 3076 Sudoku

2015-11-12 16:14 330 查看
题意:

给出一个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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息