POJ_3074_Sudoku(DancingLinksX精确覆盖)
来源:互联网 发布:陈田村拆车件淘宝店 编辑:程序博客网 时间:2024/04/30 13:23
Description
In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example,
Given some of the numbers in the grid, your goal is to determine the remaining numbers such that the numbers 1 through 9 appear exactly once in (1) each of nine 3 × 3 subgrids, (2) each of the nine rows, and (3) each of the nine columns.
Input
The input test file will contain multiple cases. Each test case consists of a single line containing 81 characters, which represent the 81 squares of the Sudoku grid, given one row at a time. Each character is either a digit (from 1 to 9) or a period (used to indicate an unfilled square). You may assume that each puzzle in the input will have exactly one solution. The end-of-file is denoted by a single line containing the word “end”.
Output
For each test case, print a line representing the completed Sudoku puzzle.
Sample Input
.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.end
Sample Output
527389416819426735436751829375692184194538267268174593643217958951843672782965341416837529982465371735129468571298643293746185864351297647913852359682714128574936
Source
DancingLinksX貌似很喜欢解决数独问题
标准数独每格填写一个数字
每行有1~9各一个
每列有1~9各一个
每小九宫有1~9各一个
正好这4个问题都要覆盖!
因此解法
1~927行代表在i格j列填k
1~81列代表i行有数字j
82~162列代表i列有数字j
163~243列代表i宫有数字j
244~324列代表i行j列有一个数字
前三个行好想,最后一个条件尤为重要!
前三个条件其实是不充分的,不能够保证所选取的方案(在i格j列填k)可行,即可能在一个格中多次填数!
而且出于效率考量,第4个条件应该放于第一位。
再有由于每一个行最多(本题解法中精确的对应4个)对应4个列为1
因此如果空间有限,则十字链表节点数可以只开到行数x4+列数即可
手写了一大堆的删除不必要行列的算法,但是对于精确一解的数独这并不很奏效
会TLE,因为处理不必要行列过程较久
代码如下
#include <iostream>#include <stdio.h>#include <string.h>using namespace std;const int MN=9*9*9+10;const int MM=9*9*4+10;const int MNN=MN*MM+MM; //最大点数//int li[MN][MM];struct DLX{ int n,m,si;//n行数m列数si目前有的节点数 //十字链表组成部分 int U[MNN],D[MNN],L[MNN],R[MNN],Row[MNN],Col[MNN]; //第i个结点的U向上指针D下L左R右,所在位置Row行Col列 int H[MN],S[MM]; //记录行的选择情况和列的覆盖情况 int ansd,ans[MN]; void init(int _n,int _m) //初始化空表 { n=_n; m=_m; for(int i=0;i<=m;i++) //初始化第一横行(表头) { S[i]=0; U[i]=D[i]=i; //目前纵向的链是空的 L[i]=i-1; R[i]=i+1; //横向的连起来 } R[m]=0;L[0]=m; si=m; //目前用了前0~m个结点 for(int i=1;i<=n;i++) H[i]=-1; } void link(int r,int c) //插入点(r,c) {// li[r][c]=1;// cout<<"link "<<r<<" "<<(r-1)/9/9<<" "<<(r-1)/9%9<<" "<<(r-1)%9+1<<endl;// if(c<82)// cout<<" "<<c<<" hang "<<(c-1)/9<<" shuzi "<<(c-1)%9+1<<endl;// else if(c>162)// cout<<" "<<c<<" gong "<<(c-162-1)/9<<" shuzi "<<(c-162-1)%9+1<<endl;// else// cout<<" "<<c<<" lie "<<(c-81-1)/9<<" shuzi "<<(c-81-1)%9+1<<endl; ++S[Col[++si]=c]; //si++;Col[si]=c;S[c]++; Row[si]=r; D[si]=D[c]; U[D[c]]=si; U[si]=c; D[c]=si; if(H[r]<0) H[r]=L[si]=R[si]=si; else { R[si]=R[H[r]]; L[R[H[r]]]=si; L[si]=H[r]; R[H[r]]=si; } } void remove(int c) //列表中删掉c列 {// if(c<82)// cout<<"remove "<<c<<" hang "<<(c-1)/9<<" shuzi "<<(c-1)%9+1<<endl;// else if(c>162)// cout<<"remove "<<c<<" gong "<<(c-162-1)/9<<" shuzi "<<(c-162-1)%9+1<<endl;// else// cout<<"remove "<<c<<" lie "<<(c-81-1)/9<<" shuzi "<<(c-81-1)%9+1<<endl; L[R[c]]=L[c];//表头操作 R[L[c]]=R[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[Col[j]]; } } void resume(int c) //恢复c列 { for(int i=U[c];i!=c;i=U[i]) for(int j=L[i];j!=i;j=L[j]) ++S[Col[U[D[j]]=D[U[j]]=j]]; L[R[c]]=R[L[c]]=c; } bool dance(int d) //主程序从d=0开始,选取了d行 { //cout<<"dance "<<d<<endl; if(R[0]==0)//全部覆盖了 { //全覆盖了之后的操作 ansd=d; return 1; } int c=R[0]; for(int i=R[0];i!=0;i=R[i]) if(S[i]<S[c]) c=i; remove(c); for(int i=D[c];i!=c;i=D[i]) { ans[d]=Row[i]; for(int j=R[i];j!= i;j=R[j]) remove(Col[j]); if(dance(d+1)) return 1; for(int j=L[i];j!=i;j=L[j]) resume(Col[j]); } resume(c); return 0; }}dlx;int sq[10][10]={ {0,0,0,0,0,0,0,0,0,0}, {0,1,1,1,2,2,2,3,3,3}, {0,1,1,1,2,2,2,3,3,3}, {0,1,1,1,2,2,2,3,3,3}, {0,4,4,4,5,5,5,6,6,6}, {0,4,4,4,5,5,5,6,6,6}, {0,4,4,4,5,5,5,6,6,6}, {0,7,7,7,8,8,8,9,9,9}, {0,7,7,7,8,8,8,9,9,9}, {0,7,7,7,8,8,8,9,9,9}};char ss[100];int isu[MM],ma[MM];int main(){ //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); while(1) { scanf("%s",ss); if(ss[0]=='e') break; memset(isu,0,sizeof(isu)); memset(ma,0,sizeof(ma)); for(int i=0;i<81;i++) if(ss[i]!='.') { int x=i/9+1; int y=i%9+1;//1~9 isu[(x-1)*9+ss[i]-'0']=1; isu[81+(y-1)*9+ss[i]-'0']=1; isu[162+(sq[x][y]-1)*9+ss[i]-'0']=1; isu[243+(x-1)*9+y]=1; } int nisu=0; for(int i=1;i<=81*4;i++) if(!isu[i]) ma[i]=++nisu; dlx.init(9*9*9,nisu); for(int i=0;i<81;i++) if(ss[i]=='.') { int x=i/9+1; int y=i%9+1;//1~9 //if(ma[243+(x-1)*9+y]) //这个格子没有填过 for(int j=1;j<=9;j++) //在xy格子中填入j { int f=1; if(ma[(x-1)*9+j]) dlx.link(i*9+j,ma[(x-1)*9+j]); else f=0; if(ma[81+(y-1)*9+j]) dlx.link(i*9+j,ma[81+(y-1)*9+j]); else f=0; if(ma[162+(sq[x][y]-1)*9+j]) dlx.link(i*9+j,ma[162+(sq[x][y]-1)*9+j]); else f=0; if(f) dlx.link(i*9+j,ma[243+(x-1)*9+y]); } } //cout<<dlx.dance(0)<<endl;// for(int j=1;j<=nisu;j++)// cout<<"\t"<<j;// cout<<endl;// for(int i=1;i<=9*9*9;i++)// {// cout<<"i "<<i;// for(int j=1;j<=nisu;j++)// if(li[i][j])// cout<<"\t"<<j;// cout<<endl;// }// for(int c=1;c<=81*4;c++)// if(!isu[c])// {// cout<<ma[c]<<" ";// if(c<82)// cout<<" hang "<<(c-1)/9<<" shuzi "<<(c-1)%9+1<<endl;// else if(c<163)// cout<<" lie "<<(c-81-1)/9<<" shuzi "<<(c-81-1)%9+1<<endl;// else if(c<244)// cout<<" gong "<<(c-162-1)/9<<" shuzi "<<(c-162-1)%9+1<<endl;// else// cout<<" yong hang"<<(c-243-1)/9<<" lie "<<(c-243-1)%9+1<<endl;// }//// continue; dlx.dance(0); //cout<<dlx.ansd<<endl; for(int i=0;i<dlx.ansd;i++) { int v=(dlx.ans[i]-1)%9+1; int p=(dlx.ans[i]-1)/9; ss[p]=v+'0'; } printf("%s\n",ss); } return 0;}
反而倒是比较原始的算法
已经填了数字的格子只加1行
没有填数字的格子加9行的方法,可以轻松通过
#include <iostream>#include <stdio.h>#include <string.h>using namespace std;const int MN=9*9*9+10;const int MM=9*9*4+10;const int MNN=MN*MM+MM; //最大点数struct DLX{ int n,m,si;//n行数m列数si目前有的节点数 //十字链表组成部分 int U[MNN],D[MNN],L[MNN],R[MNN],Row[MNN],Col[MNN]; //第i个结点的U向上指针D下L左R右,所在位置Row行Col列 int H[MN],S[MM]; //记录行的选择情况和列的覆盖情况 int ansd,ans[MN]; void init(int _n,int _m) //初始化空表 { n=_n; m=_m; for(int i=0;i<=m;i++) //初始化第一横行(表头) { S[i]=0; U[i]=D[i]=i; //目前纵向的链是空的 L[i]=i-1; R[i]=i+1; //横向的连起来 } R[m]=0;L[0]=m; si=m; //目前用了前0~m个结点 for(int i=1;i<=n;i++) H[i]=-1; } void link(int r,int c) //插入点(r,c) { //cout<<"link r "<<r<<" c "<<c<<endl; ++S[Col[++si]=c]; //si++;Col[si]=c;S[c]++; Row[si]=r; D[si]=D[c]; U[D[c]]=si; U[si]=c; D[c]=si; if(H[r]<0) H[r]=L[si]=R[si]=si; else { R[si]=R[H[r]]; L[R[H[r]]]=si; L[si]=H[r]; R[H[r]]=si; } } void remove(int c) //列表中删掉c列 { //cout<<"remove "<<c<<endl; L[R[c]]=L[c];//表头操作 R[L[c]]=R[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[Col[j]]; } } void resume(int c) //恢复c列 { //cout<<"resume "<<c<<endl; for(int i=U[c];i!=c;i=U[i]) for(int j=L[i];j!=i;j=L[j]) ++S[Col[U[D[j]]=D[U[j]]=j]]; L[R[c]]=R[L[c]]=c; } bool dance(int d) //选取了d行 { if(R[0]==0)//全部覆盖了 { //全覆盖了之后的操作 ansd=d; return 1; } int c=R[0]; for(int i=R[0];i!=0;i=R[i]) if(S[i]<S[c]) c=i; remove(c); for(int i=D[c];i!=c;i=D[i]) { ans[d]=Row[i]; for(int j=R[i];j!= i;j=R[j]) remove(Col[j]); if(dance(d+1)) return 1; for(int j=L[i];j!=i;j=L[j]) resume(Col[j]); } resume(c); return 0; }}dlx;//可以不用sq矩阵,直接用关系算出在哪个小九宫int sq[9][9]={ {1,1,1,2,2,2,3,3,3}, {1,1,1,2,2,2,3,3,3}, {1,1,1,2,2,2,3,3,3}, {4,4,4,5,5,5,6,6,6}, {4,4,4,5,5,5,6,6,6}, {4,4,4,5,5,5,6,6,6}, {7,7,7,8,8,8,9,9,9}, {7,7,7,8,8,8,9,9,9}, {7,7,7,8,8,8,9,9,9}};char ss[100];int main(){ //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); while(1) { scanf("%s",ss); if(ss[0]=='e') break; dlx.init(9*9*9,9*9*4); for(int i=0;i<81;i++) if(ss[i]!='.') { int x=i/9; int y=i%9;//0~8 //cout<<x<<y<<endl; dlx.link(i*9+ss[i]-'0',x*9+ss[i]-'0'); dlx.link(i*9+ss[i]-'0',81+y*9+ss[i]-'0'); dlx.link(i*9+ss[i]-'0',162+(sq[x][y]-1)*9+ss[i]-'0'); dlx.link(i*9+ss[i]-'0',243+x*9+y+1); } else { for(int j=1;j<=9;j++) { int x=i/9; int y=i%9;//0~8 //cout<<x<<y<<endl; dlx.link(i*9+j,x*9+j); dlx.link(i*9+j,81+y*9+j); dlx.link(i*9+j,162+(sq[x][y]-1)*9+j); dlx.link(i*9+j,243+x*9+y+1); } } dlx.dance(0); for(int i=0;i<dlx.ansd;i++) { int v=(dlx.ans[i]-1)%9+1; int p=(dlx.ans[i]-1)/9; ss[p]=v+'0'; } printf("%s\n",ss); } return 0;}
- POJ_3074_Sudoku(DancingLinksX精确覆盖)
- ZOJ_3029_TreasureMap(DancingLinksX精确覆盖)
- HUST_1017_ExactCover(DancingLinksX精确覆盖模板题)
- LA_2659_POJ_3076_ZOJ_3122_Sudoku(DancingLinksX精确覆盖,数独题模板)
- HDU_4069_SquigglySudoku(DancingLinksX精确覆盖+BFS)
- POJ_1084_SquareDestroyer(DancingLinksX重复覆盖)
- HDU_4979_ASimpleMathProblem.(DancingLinksX重复覆盖)
- HDU_2295_Radar(DancingLinksX重复覆盖+二分)
- HDU_3335_Divisibility(DancingLinksX重复覆盖||dfs||暴力)
- HDU_5046_Airport(DancingLinksX重复覆盖+二分)
- FZU_1686_神龙的难题(DancingLinksX重复覆盖)
- 精确覆盖
- poj 3074/poj 3076(精确覆盖)
- poj 3740 DLX(精确覆盖)
- 01精确覆盖(DLX类)
- POJ 3074 Sudoku(DLX+精确覆盖)
- ZOJ 3209 Treasure Map(精确覆盖)
- ZOJ3209 Treasure Map(DLX精确覆盖)
- readLine方法的简单练习,回答2016-04-27 的帖子的问题
- HDU 1542 Atlantis
- zabbix3.0 监控mysql服务器性能实现过程
- 课堂笔记之线程池
- Lab4: bootloader
- POJ_3074_Sudoku(DancingLinksX精确覆盖)
- htoi(十六进制转十进制)
- AFN
- Spark学习三:Spark Schedule以及idea的安装和导入源码
- Spark学习四:网站日志分析案例
- [Python]dict,set
- Java泛型
- google搜索引擎核心PageRank
- 走进VR游戏开发的世界