首页 > 代码库 > 使用双向十字链表(或Dancing Links)解数独游戏
使用双向十字链表(或Dancing Links)解数独游戏
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; struct Data { void assign(int x,int y,int z) { row=x; col=y; val=z; } int row,col,val; } data[730]; struct Node { Node(int x=0,int y=0): row(x),col(y),up(this),down(this),left(this),right(this) {} int row,col; Node *up,*down,*left,*right; }**rowHead,**colHead; char tempData[9][10]; int rowCounter[730],colCounter[325]; int ans[10][10]; int belong[10][10],belong1[10][10],belong2[10][10],belong3[10][10],belong4[10][10]; int belongX[325],belongY[325]; void init() { rowHead=new Node*[730],colHead=new Node*[325]; for(int i=0; i<=729; i++)rowHead[i]=new Node(i,0); for(int i=0; i<=324; i++)colHead[i]=new Node(0,i); for(int i=0; i<=729; i++) if(i&&i!=729) { rowHead[i]->up=rowHead[i-1]; rowHead[i]->down=rowHead[i+1]; } else if(!i) { rowHead[i]->up=rowHead[729]; rowHead[i]->down=rowHead[i+1]; } else { rowHead[i]->up=rowHead[i-1]; rowHead[i]->down=rowHead[0]; } for(int i=0; i<=324; i++) if(i&&i!=324) { colHead[i]->left=colHead[i-1]; colHead[i]->right=colHead[i+1]; } else if(!i) { colHead[i]->left=colHead[324]; colHead[i]->right=colHead[i+1]; } else { colHead[i]->left=colHead[i-1]; colHead[i]->right=colHead[0]; } memset(rowCounter,0,sizeof(rowCounter)); memset(colCounter,0,sizeof(colCounter)); memset(ans,0,sizeof(ans)); } void link(int x,int y) { Node *newNode=new Node(x,y); if(rowHead[x]->right==rowHead[x]) { newNode->left=newNode->right=rowHead[x]; rowHead[x]->left=rowHead[x]->right=newNode; rowCounter[x]++; } else { for(Node *i=rowHead[x]->right;; i=i->right) if(newNode->col<i->col||!i->col) { newNode->left=i->left; newNode->right=i; i->left->right=newNode; i->left=newNode; rowCounter[x]++; break; } } if(colHead[y]->down==colHead[y]) { newNode->up=newNode->down=colHead[y]; colHead[y]->up=colHead[y]->down=newNode; colCounter[y]++; } else { for(Node *i=colHead[y]->down;; i=i->down) if(newNode->row<i->row||!i->row) { newNode->up=i->up; newNode->down=i; i->up->down=newNode; i->up=newNode; colCounter[y]++; break; } } } void remove(Node *&x) { x->left->right=x->right,x->right->left=x->left; for(Node *i=x->down; i!=x; i=i->down) for(Node *j=i->right; j!=i; j=j->right) if(j!=rowHead[i->row]) { j->up->down=j->down; j->down->up=j->up; colCounter[j->col]--; } } void resume(Node *&x) { for(Node *i=x->up; i!=x; i=i->up) for(Node *j=i->left; j!=i; j=j->left) if(j!=rowHead[i->row]) { colCounter[j->col]++; j->down->up=j; j->up->down=j; } x->right->left=x,x->left->right=x; } bool dance() { if(colHead[0]->right==colHead[0])return true; int best=2147483647; Node *bestNode; for(Node *i=colHead[0]->right; i!=colHead[0]; i=i->right) if(best>colCounter[i->col]) { best=colCounter[i->col]; bestNode=i; } remove(bestNode); for(Node *i=bestNode->down; i!=bestNode; i=i->down) { ans[data[i->row].row][data[i->row].col]=data[i->row].val; for(Node *j=i->right;j!=i;j=j->right) if(j!=rowHead[i->row]) remove(colHead[j->col]); if(dance())return true; for(Node *j=i->left;j!=i;j=j->left) if(j!=rowHead[i->row]) resume(colHead[j->col]); } resume(bestNode); return false; } int main() { for(int i=1; i<=9; i++) for(int j=1; j<=9; j++) belong[i][j]=3*((i-1)/3)+(j-1)/3+1, belong1[i][j]=9*(i-1)+j, belong2[i][j]=9*(i+8)+j, belong3[i][j]=9*(i+17)+j, belong4[i][j]=9*(i+26)+j; for(int i=1; i<=81; i++) belongX[i]=(i-1)/9+1, belongY[i]=(i-1)%9+1; for(int i=82; i<=162; i++) belongX[i]=(i-82)/9+1, belongY[i]=(i-82)%9+1; for(int i=163; i<=243; i++) belongX[i]=(i-163)/9+1, belongY[i]=(i-163)%9+1; for(int i=244; i<=324; i++) belongX[i]=(i-244)/9+1, belongY[i]=(i-244)%9+1; int T; scanf("%d",&T); getchar(); for(int countT=1; countT<=T; countT++) { init(); int countRow=0; for(int i=0; i<9; i++) { gets(tempData[i]); for(int j=0; j<9; j++) if(tempData[i][j]!='0') { int tempNumber=tempData[i][j]-'0'; countRow++; data[countRow].assign(i+1,j+1,tempNumber); link(countRow,belong1[i+1][j+1]); link(countRow,belong2[i+1][tempNumber]); link(countRow,belong3[j+1][tempNumber]); link(countRow,belong4[belong[i+1][j+1]][tempNumber]); } else { int counter=9,tempNumber=1; while(counter--) { countRow++; data[countRow].assign(i+1,j+1,tempNumber); link(countRow,belong1[i+1][j+1]); link(countRow,belong2[i+1][tempNumber]); link(countRow,belong3[j+1][tempNumber]); link(countRow,belong4[belong[i+1][j+1]][tempNumber]); tempNumber++; } } } if(dance()) { for(int i=1; i<=9; i++) { for(int j=1; j<=9; j++)printf("%d",ans[i][j]); putchar('\n'); } } } return 0; }
之前水平不够,写了一个不加任何优化,直接搜的解数独程序。
现在水平提高,学会如何用双向十字链表优化时间复杂度来解数独游戏,还不能直接使用它,必须将它转化成“精确覆盖模型”。
为什么?
我尝试着写了一个单纯用双向十字链表来解数独游戏的程序,它有优点也有缺点。之前暴搜的那个程序解特别稀疏的数独就会超时,比如:全是空儿的数独。但是单纯用双向十字链表的程序能够解决这样“全是空儿”的数独,但是还不够~对于有些数独,尤其是唯一解的数独,也同样会超时的,比如下面这个很难很难的数独:
800 000 000
003 600 000
070 090 200
050 007 000
000 045 700
000 100 030
001 000 068
008 500 010
090 000 400
上面的数独对于单纯使用双向十字链表的程序就是克星,因为它的解用一种哲学上的思维来说就是:这个问题的解被排在了后面。对的,深度优先搜索都是一直搜到底的,对于有些问题解被“排在后面”的,深度优先搜索就不行了。
到底什么地方还能优化呢?
其实我们在单纯使用双向十字链表而不进行模型转化的时候,我们与以往搜索唯一的不同就在于我们能够直接找到哪些有空儿、哪些没有空儿,但是有一点:我们还是从1到9枚举了每一个数字,然后对于每个数字通过之前打上的标记判断能不能填,虽然只有9个数字,但是这9个数字的枚举是放在递归层次里的,非常费时。
现在我们不仅需要能够直接知道哪些空儿可以填,我们还需要直接知道哪些数字是可以填的,这样可以加快进程~~~
怎样转化成精确覆盖模型?
构造这样的矩阵:
对于第1~81列:
第1列:在(1,1)填了数字
第2列:在(1,2)填了数字
。。。。。。
第n列:。。。。。。
映射:row=(n-1)/9+1,col=(n-1)%9+1,n=9*(row-1)+col
对于第81~162列:
第1列:在第1行填了数字1
第2列:在第2行填了数字2
。。。。。。
第n列:。。。。。。
映射:row=(n-82)/9+1,num=(n-82)%9+1,n=9*(row+8)+num
对于第163~243列:
第1列:在第1列填了数字1
第2列:在第2列填了数字2
。。。。。。
第n列:。。。。。。
映射:col=(n-163)/9+1,col=(n-163)%9+1,n=9*(col+17)+num
对于第244~324列:
第1列:在第1宫填了数字1
第2列:在第1宫填了数字2
。。。。。。
第n列:。。。。。。
映射:grid=(n-244)/9+1,num=(n-244)%9+1,n=9*(grid+26)+num
对于一个给出的矩阵,如果在(x,y)处是3,那么就插入这样一行
它的9*(x-1)+y,9*(x+8)+3,9*(y+17)+3,9*(grid(x,y),26)+num(坐标映射到宫:3*((x-1)/3)+(y-1)/3+1)
以上四列都是1,但是插入行的时候要记录对应行所代表的意义是什么,这样在搜索时就能很轻易得到信息。
如果是空儿,那么就按照上面的方法在对应坐标将1~9依次插入,总共就是4*9=36个节点。
接下来直接解精确覆盖就可以了,通过选的行的编号,可以知道在那个位置填入几,当然这也要预处理一下,我用的方法就是直接将信息放在Data结构体中,当然可能也有公式,我就不推了。
写的有些潦草,不如网上一些大牛写的好。
下面是一个测试样例和结果:
1
800000000
003600000
070090200
050007000
000045700
000100030
001000068
008500010
090000400
812753649
943682175
675491283
154237896
369845721
287169534
521974368
438526917
796318452
时间相当快,可见双向十字链表+精确覆盖模型的解题速度之快。
不过感觉本文还是写挫了,凑合看吧。