poj3074 poj3076 数独问题 dancing links

来源:互联网 发布:国标网络协议 编辑:程序博客网 时间:2024/05/26 02:19

当初学习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[N],L[N],U[N],D[N],C[N],Row[N];int S[M],O[M];int n,m;int ans; // 需要拿掉几行int size; //节点个数struct node{int i,j,x;}res[N];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[n].i = i;res[n].j = j;res[n].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;}