转载自:http://fayaa.com/code/view/1738/
把debug文件夹中的exe文件放在指定路径,这里放在F盘
然后打开cmd文件输入
F:\53.exe 030000000010200060000600400004000800007000000000001003000460005000800000050000001
输出结果
数独测试案例
030000000010200060000600400004000800007000000000001003000460005000800000050000001
436198572719254368285637419194372856327586194568941723971463285642815937853729641
001000400000000002000045000020060310000001020000097680000006100000009000000000205
351972468746183952892645731427568319968431527135297684579826143214359876683714295
351972468946183752872645931429568317768431529135297684597826143214359876683714295
//from: http://code.google.com/p/klsudoku/source/checkout
//半瓶墨水修改于 2009 Sept 18
//References
// http://en.wikipedia.org/wiki/Knuth's_Algorithm_X
// http://en.wikipedia.org/wiki/Dancing_Links
// http://en.wikipedia.org/wiki/Exact_cover#Sudoku
#include
using namespace std;
#define RR 729 //81*9 创建出9个舞蹈链,分别代表填入的数字?
#define CC 324 //81*4 约束条件?
#define INF 1000000000
int mem1[81+1];
int mem2[81+1];
int *mem = mem1;
char ch[81+1];
int cnt[CC+1];
int scnt=0; //solution count
struct node {
int r,c;
node *up;
node *down;
node *left;
node *right;
}head,all[RR*CC+99],row[RR],col[CC];
int all_t;
//用链表解释就是一直插在第一个结点,以前的结点右推。
inline void link(int r,int c) {//第r行,第c列
cnt[c]++;//第c列的结点增加了一个
node *t=&all[all_t++];//将指针指向下一个,就像线性表添加元素一样
t->r=r;//t的行数等于r
t->c=c;//t的列数等于c
t->left=&row[r];//t的左边等于第r行结点
t->right=row[r].right;//t的右边等于第r行结点的右边
t->left->right=t; //t的左边的右边等于t
t->right->left=t; //t的右边的左边等于t
t->up=&col[c];//t的上边等于第c列结点
t->down=col[c].down;//t的下边等于第c列下边
t->up->down=t;//t的上边的下边等于t
t->down->up=t;//t的下边的上边等于t
}
inline void remove(int c) {//删除这列的结点和结点所在行的结点
node *t,*tt;
//删除列结点
col[c].right->left=col[c].left;//该列结点的右边的左边等于该列结点的左边
col[c].left->right=col[c].right;//该列结点的左边的右边等于该列结点的右边
for(t=col[c].down;t!=&col[c];t=t->down) {//访问该列的所有结点 直到回到列结点
for(tt=t->right;tt!=t;tt=tt->right) {//访问该列所有结点所在的每一行
cnt[tt->c]--;//该列的结点减少一个
//删除该结点所在行中的一个结点
tt->up->down=tt->down;//该结点的上边的下边等于该结点的下边
tt->down->up=tt->up;//该结点的下边的上边等于该结点的上边
}
//删除该结点
t->left->right=t->right;//t的左边的右边等于t的右边
t->right->left=t->left;//t的右边的左边等于t的左边
}
}
inline void resume(int c) {
node *t,*tt;
//遍历该列结点
for(t=col[c].down;t!=&col[c];t=t->down) {
t->right->left=t;//恢复t结点
t->left->right=t;//恢复t结点
for(tt=t->left;tt!=t;tt=tt->left) {//一直访问左边,直到回到t
cnt[tt->c]++;
tt->down->up=tt;
tt->up->down=tt;
}
}
col[c].left->right=&col[c];
col[c].right->left=&col[c];
}
void print_solution(int*m){
int ans[81];
for(int i=1;i<=81;i++) {
int t=m[i]/9%81;
int val=m[i]%9+1;
ans[t]=val;
}
for(i=0;i<81;i++)
printf("%d",ans[i]);
printf("\n");
}
void solve(int k)
{
if(head.right==&head) {//got one solution
if (!scnt) {//如果是第一个解决方案的话
memcpy(mem2, mem1, sizeof(mem1));
mem = mem2;
}
scnt++;
return;//这里第一种解决方案得到后,返回继续 选行 来看有没有第二种解决方案
}
node*t,*tt;
int min=INF,tc;//min=1000000000;
//从头结点开始一直向右 直到回到头结点
//挑选结点数量最小的那一行,如果数量小于等于1直接用这行
cout<right) {
if(cnt[t->c]c];
tc=t->c;
if(min<=1)break;
}
}
//min==0的时候会把列删除然后再把列恢复然后返回,说明之前选错了行导致出现了结点为0的列,重新往下选择一行。
remove(tc);//移除这一列
//扫描这一列 直到 回到列结点
for(t=col[tc].down;t!=&col[tc];t=t->down) {
mem[k]=t->r;//mem[k]存储t的行数,最后可以通过行数来推断数独的几行几列填入了哪个数字
// cout<<&(t->left)<<' '<left->r<<' '<left->c<<' '<<&(t->left->right)<<' '<left->right->r<<' '<left->right->c<<' '<<&t<<' '<r<<' '<c<left->right->right->r<<' '<left->right->right->c<left->right=t;//经检查这两个指针所指向的地址不同
//开始访问t的右边 直到回到t。但是由于t在remove(tc)的过程中左右被跳过,所以tt!=t可能会一直成立,所以需要上一步来保证能回到t
for(tt=t->right;tt!=t;tt=tt->right) {
remove(tt->c);//移除该行中所有带结点的列
}
// cout<<&(t->left)<<' '<left->r<<' '<right->c<<' '<<&(t->left->right)<<' '<left->right->r<<' '<left->right->c<<' '<<&t<<' '<r<<' '<c<right)<<' '<right->r<<' '<right->c<left->right=t->right;//t左边的右边等于t的右边 删除t
solve(k+1);//给下一个找行
if (scnt >= 2)//如果解决方案大于等于两个
return;
//同上,避免死循环
t->right->left=t;
//恢复所有被删除的列
for(tt=t->left;tt!=t;tt=tt->left) {
resume(tt->c);
}
//恢复t结点
t->right->left=t->left;
}
//恢复tc列
resume(tc);
return;//一旦跑出来了说明之前选错了行,且如果一直回溯到一开始然后没有更多的行可以选择且scnt为0就说明没有解决方案
}
//int mem1[] ---> 0-81
//int mem2[] ---> 0-81
//int *mem ---> mem1[]
//char ch ---> 0-81
//int cnt[] ---> 0-324 用于记录0-324列,这一列有多少个结点
//int scnt ---> 0
//node head ---> head node
//node all[] ---> 0-236294 构建729*324+99列的舞蹈链
//node row[] ---> 0-728 构建729列的舞蹈链,用于1-9的填入,每个数字用81列来表示
//node col[] ---> 0-323 构建324列的舞蹈链,用于满足4个约束条件
//int RR ---> 729
//int CC ---> 324
int main(int argc, char**argv) {
if (argc != 2) return 1;//第一个元素是程序所在路径 第二个元素是数独,用0表示空缺位置,81个数字构成第二个元素
const char *p = argv[1];//将指针指向第二个元素
size_t len = strlen(p);//获取p指针指向的数组长度
if (len!=81) return 1;//如果不是81个数字就不是数独
int i;
for (i=0;i<81;i++) {//遍历这81个字母
if ((*p) == '0')//如果访问到0
ch[i] = '.';//用.号代替
else
ch[i] = *p;//访问到数字
p++;//访问81个字母
}
ch[81] = '\0';//第81位赋终止符
if(ch[0]=='e')return 1;//如果第一位是字母'e'就结束程序
all_t=1;//----------------------------------------------------------all_t赋值为1
memset(cnt,0,sizeof(cnt));//把cnt整个赋值为0
head.left=&head;//头结点的左边是头结点
head.right=&head;//头结点的右边是头结点
head.up=&head;//头结点的上面是头结点
head.down=&head;//头结点的下面是头结点
head.r=RR;//-------------------------------------------------------行数等于729
head.c=CC;//-------------------------------------------------------列数等于324
//for 0-323
for(i=0;iright=&col[i];//它的左边的右边等于自己
col[i].right->left=&col[i];//它的右边的左边等于自己
}
//for 0-728
for(i=0;idown=&row[i];//它的上边的下边等于自己
row[i].down->up=&row[i];//它的下边的上边等于自己
}
//for 0-728
//访问所有行,数独舞蹈链中的第i行 表示 数独中的第r行第c列中填入数字val
for(i=0;i数独行
int c=i/9%9;//0-8 c为0 9-17 c为1 18-26 c为2 …… 72-80为8 循环直至720-728为8 81个为一周期 表示数独中的列 映射:舞蹈链行->数独列
int val=i%9+1;//0为1 1为2 2为3 …… 8为9 9个为一周期 表示数字1-9 映射:舞蹈链行->1-9数字
if(ch[r*9+c]=='.'||ch[r*9+c]==val+'0') {//r表示第r行,c表示第c列,如果数独的第r行第c列是'.'号或是数字1-9
//如果数独的第r行第c列是'.'号则它的所有行都建立舞蹈链结点
//如果数独的第r行第c列是数字则它的指定行都建立舞蹈链结点
link(i,r*9+val-1);//处理约束条件1:每个格子只能填一个数字 0-80列
link(i,81+c*9+val-1);//处理约束条件2:每行1-9这9个数字只能填一个 81-161列
int tr=r/3;
int tc=c/3;
link(i,162+(tr*3+tc)*9+val-1);//处理约束条件3:每列1-9的这9个数字都得填一遍
link(i,243+r*9+c);//处理约束条件4:每宫1-9的这9个数字都得填一遍
}
}
//for 0-728
//把728个行结点全部删除
for(i=0;iright=row[i].right;//每一行左边的右边等于行数的右边
row[i].right->left=row[i].left;//每一行右边的左边等于行数的左边
}
solve(1);//调用solve函数
printf("\n");
if (scnt>1){
print_solution(mem1);
print_solution(mem2);
printf("\n2 or mroe solutions found\n");
}else if (scnt==1) {
print_solution(mem1);
printf("\none solution found\n");
}else {
printf("\nNO solution\n");
}
return 0;
}