您的位置:首页 > 其它

poj3074 poj3076 数独问题 dancing links

2013-08-02 20:04 681 查看
当初学习dancing links的最大目的就是为了解决数独的问题吧,但是经过这两天的学习之后发现dancing links的功能远不止于此,看来是得多挖掘一下。

当然本文还是介绍解数独的做法。

首先,需要把数度问题转化为一个最小覆盖问题:

1、每个空格最终都会填上一个数,而且有1,2,3,4,5,6,7,8,9这九种可能(介绍3*3的,其他大小的同理),我们可以把每个空格中填写的数字情况看成是dancing links中的行,那么最多就会有9*9*9个行了

2、列代表什么含义呢?我们填写数字是为了达到这样的目的:每行包含1-9,每列包含1-9,9个3*3小块包含1-9,以及81个格子填满,所以,可以这么设置列:

前面81列表示对应的格子里是否填数,然后9*9=81列表示,第几行里是否已经填了几,例子:第5行里已经有4了,那么就是在这81列中的第9*(5-1)+4列;再往后还是81列,只不过对应第几列里是否有几;最后81列则是表示第几个3*3小块里是否有几

然后,从1中所谓的行中选出81行(因为每行代表一个格子),使得这81行恰好覆盖了这里的81*4列

问题转化完成。

然后就是对数独的处理,在读入数独的时候,如果某个格子上已经有数字填了,那么该格子相当于就是对应一行,直接插入;如果没填数,那么要假设这个格子填1-9,所以得加入九行。

更新列时:每一个格子确定一个数,那么一定会在4个列中增加元素(81*4),每个81列中都会出现一个(原因见上面的分析)

等dancing links建立完毕之后,就是一个exact cover问题了,借用之前的代码即可

以下是我解决poj3076的代码(16*16的数独)

我这里只要改变LEN这个常量值就可以计算不同大小的数独,如16*16就是4,9*9就是3,N*N就是根号N

#include <cstdio>
#include <cstring>
const int LEN = 4;
const int SIZE =LEN*LEN;
const int M = SIZE*SIZE*4+10;
const int N = SIZE*SIZE*SIZE*4+M+10;

int R
,L
,U
,D
,C
,Row
;
int S[M],O[M];
int n,m;
int ans; // 需要拿掉几行
int size; //节点个数

struct node
{
int i,j,x;
}res
;

void init()
{
memset(S,0,sizeof(S)); //初始的时候每列上1的个数为0

//链表初始化
for(int i=1; i<=m; i++)
L[i+1] = R[i-1] = U[i] = D[i] = i;
R[m] = 0; //0表示表头
size = m + 1; //新的节点的下标是从m+1开始的,当然也可以随机的设置一个更大的值,没有影响
}

//删除第c列,以及该列中元素对应的所有行
void remove(int c)
{
R[L[c]] = R[c];
L[R[c]] = L[c];
for(int i=D[c]; i!=c; i=D[i])
for(int j=R[i]; j!=i; j=R[j])
{
U[D[j]] = U[j];
D[U[j]] = D[j];
S[C[j]]--;
}
}

//恢复第c列,以及该列中元素对应的所有行
void resume(int c)
{
for(int i=U[c]; i!=c; i=U[i])
for(int j=L[i]; j!=i; j=L[j])
{
U[D[j]] = D[U[j]] = j;
S[C[j]]++;
}
R[L[c]] = L[R[c]] = c;
}

bool dfs(int k) //k表示当前已经拿掉的行数
{
if(R[0]==0) //表示所有的列已经被拿完
{
ans = k;
return 1;
}
int min = M,c=-1;
//选取元素个数最少的列
for(int i=R[0]; i!=0; i=R[i])
if(S[i]<min) min = S[i],c = i;
remove(c);
for(int i=D[c]; i!=c; i=D[i])
{
for(int j=R[i]; j!=i; j=R[j])
remove(C[j]);
O[k] = Row[i]; //当前拿掉的是i那个元素对应的行
if(dfs(k+1)) return 1;
for(int j=L[i]; j!=i; j=L[j])
resume(C[j]);
}
resume(c);
return 0;
}

void insertCol(int col)
{
C[size] = col;
U[size] = U[col];
D[U[col]] = size;
U[col] = size;
D[size] = col;
S[col]++;
}

void insertRow(int &rowhead)
{
if(rowhead==-1)
{
L[size] = R[size] = size;
rowhead = size;
}
else
{
L[size] = L[rowhead];
R[L[rowhead]] = size;
L[rowhead] = size;
R[size] = rowhead;
}
}

void addNode(int i,int j,int y)
{
int rowhead = -1;
insertCol(SIZE*(i-1)+j);
insertRow(rowhead);
Row[size] = n;
size++;
insertCol(SIZE*SIZE+(i-1)*SIZE+y);
insertRow(rowhead);
Row[size] = n;
size++;
insertCol(SIZE*SIZE*2+(j-1)*SIZE+y);
insertRow(rowhead);
Row[size] = n;
size++;
insertCol(SIZE*SIZE*3+(((i+LEN-1)/LEN-1)*LEN+(j+LEN-1)/LEN-1)*SIZE+y);
insertRow(rowhead);
Row[size] = n;
size++;
res
.i = i;
res
.j = j;
res
.x = y;
n++;
}

int main()
{
//freopen("in","r",stdin);
char s[300];
m = SIZE*SIZE*4;
int cnt = 0;
while(cnt==0||gets(s+1))
{
cnt++;
for(int i=0; i<SIZE; i++)
gets(s+i*SIZE+1);
init();
n = 1;
for(int i=1; i<=SIZE; i++)
for(int j=1; j<=SIZE; j++)
{
char x = s[SIZE*(i-1)+j];
if(x!='-')
{
int y = x - 'A' + 1;
addNode(i,j,y);
}
else
{
for(int y=1; y<=SIZE; y++) addNode(i,j,y);
}
}
dfs(0);
int tmp[SIZE+1][SIZE+1];
for(int i=0; i<ans; i++)
{
int l = O[i];
tmp[res[l].i][res[l].j] = res[l].x;
}
if(cnt>1) puts("");
for(int i=1; i<=SIZE; i++,puts(""))
for(int j=1; j<=SIZE; j++)
printf("%c", tmp[i][j]+'A'-1);

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: