題意:
給出一個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;
}