---恢复内容开始---
最近在一个数独网站玩数独游戏,网站地址为:http://www.sudokufans.org.cn/。
由于自己数独能力不是特别强,解题比较慢,但是自己是程序猿,所以,我想,自己写个数独计算器吧,让电脑帮我去算得了。
由于我是C程序猿,所以第一步要做的是,先不管界面,做一个黑底白字的win32控制台应用程序,用于验证自己的算法。
好了,开工,所以做一个简单的儿童4阶数独,如图:
,
我程序是这样使用的,首先,在程序的同目录下放一个input.txt文件用于输入,其中,未知数用0表示,每个数之间一个空格,例如上图的input.txt文件的内容为:
4 0 3 0
3 0 0 0
0 0 0 0
0 0 0 1
然后点击根据我代码生成的程序,就得到输出结果。
。
由于数据比较简单,比较才是4X4的数独,所以也没做什么优化,就是通过数据结构里图论中的DFS从第一个未知数开始,至上而下,从左到右依次枚举每种可能解。
代码如下:
1 #include <iostream> 2 #include <fstream> 3 #include <set> 4 #include <vector> 5 using namespace std; 6 7 //#define DEBUG 8 9 vector<vector<int> > Sudo; 10 11 void PrintSudo() 12 { 13 for (int i=0; i<4; i++) 14 { 15 for (int j=0; j<4; j++) 16 { 17 cout << Sudo[i][j] << " "; 18 } 19 cout << endl; 20 } 21 } 22 23 bool DFS(int X, int Y) 24 { 25 if (Y >= 4) 26 { 27 return true; 28 } 29 30 if (X >= 4) 31 { 32 return DFS(0, Y + 1); 33 } 34 35 if (Sudo[Y][X] != 0) 36 { 37 return DFS(X + 1, Y); 38 } 39 40 set<int> HaveExist; 41 int i, j; 42 43 for (i=0; i<4; i++) 44 { 45 if (Sudo[Y][i] != 0) 46 { 47 HaveExist.insert(Sudo[Y][i]); //同行中已存在的数 48 } 49 50 if (Sudo[i][X] != 0) 51 { 52 HaveExist.insert(Sudo[i][X]); //同列中已存在的数 53 } 54 } 55 56 for (i=Y/2*2; i<Y/2*2 + 2; i++) 57 { 58 for (j=X/2*2; j<X/2*2 + 2; j++) 59 { 60 if (Sudo[i][j] != 0) 61 { 62 HaveExist.insert(Sudo[i][j]); 63 } 64 } 65 } 66 67 68 for (i=1; i<=4; i++) 69 { 70 if (HaveExist.find(i) == HaveExist.end()) //数字i在当前数独还未存在,是候选数 71 { 72 Sudo[Y][X] = i; 73 #ifdef DEBUG 74 cout << "X=" << X << ", Y=" << Y << endl; 75 cout << "已存在的数:"; 76 77 for (set<int>::iterator it=HaveExist.begin(); it!=HaveExist.end(); it++) 78 { 79 cout << *it << " "; 80 } 81 cout << endl; 82 cout << "将Sudo[" << Y << "][" << X << "]设置成" << i << endl; 83 PrintSudo(); 84 #endif 85 if (DFS(X+1, Y)) 86 { 87 return true; 88 } 89 } 90 } 91 92 Sudo[Y][X] = 0; 93 return false; 94 } 95 96 int main() 97 { 98 ifstream cin("input.txt"); 99 100 101 102 for (int i=0; i<4; i++) 103 { 104 vector<int> vecTmp; 105 for (int j=0; j<4; j++) 106 { 107 int nTmp; 108 109 cin >> nTmp; 110 vecTmp.push_back(nTmp); 111 } 112 Sudo.push_back(vecTmp); 113 vecTmp.clear(); 114 } 115 116 if(!DFS(0, 0)) 117 { 118 cout << "输入数据有误" << endl; 119 } 120 121 for (int i=0; i<4; i++) 122 { 123 for (int j=0; j<4; j++) 124 { 125 cout << Sudo[i][j] << " "; 126 } 127 cout << endl; 128 } 129 130 while (true) 131 { 132 133 } 134 135 return 0; 136 }
好了,尝试了4X4的数独之后,再来尝试9X9的数独,首先,我们先简单的将之前的4改成9(当然,同区域的56和58的2改成3)看看情况会怎么样;
看代码
1 #include <iostream> 2 #include <fstream> 3 #include <set> 4 #include <vector> 5 using namespace std; 6 7 //#define DEBUG 8 9 vector<vector<int> > Sudo; 10 11 void PrintSudo() 12 { 13 for (int i=0; i<9; i++) 14 { 15 for (int j=0; j<9; j++) 16 { 17 cout << Sudo[i][j] << " "; 18 } 19 cout << endl; 20 } 21 } 22 23 bool DFS(int X, int Y) 24 { 25 if (Y >= 9) 26 { 27 return true; 28 } 29 30 if (X >= 9) 31 { 32 return DFS(0, Y + 1); 33 } 34 35 if (Sudo[Y][X] != 0) 36 { 37 return DFS(X + 1, Y); 38 } 39 40 set<int> HaveExist; 41 int i, j; 42 43 for (i=0; i<9; i++) 44 { 45 if (Sudo[Y][i] != 0) 46 { 47 HaveExist.insert(Sudo[Y][i]); //同行中已存在的数 48 } 49 50 if (Sudo[i][X] != 0) 51 { 52 HaveExist.insert(Sudo[i][X]); //同列中已存在的数 53 } 54 } 55 56 for (i=Y/3*3; i<Y/3*3 + 3; i++) 57 { 58 for (j=X/3*3; j<X/3*3 + 3; j++) 59 { 60 if (Sudo[i][j] != 0) 61 { 62 HaveExist.insert(Sudo[i][j]); 63 } 64 } 65 } 66 67 68 for (i=1; i<=9; i++) 69 { 70 if (HaveExist.find(i) == HaveExist.end()) //数字i在当前数独还未存在,是候选数 71 { 72 Sudo[Y][X] = i; 73 #ifdef DEBUG 74 cout << "X=" << X << ", Y=" << Y << endl; 75 cout << "已存在的数:"; 76 77 for (set<int>::iterator it=HaveExist.begin(); it!=HaveExist.end(); it++) 78 { 79 cout << *it << " "; 80 } 81 cout << endl; 82 cout << "将Sudo[" << Y << "][" << X << "]设置成" << i << endl; 83 PrintSudo(); 84 #endif 85 if (DFS(X+1, Y)) 86 { 87 return true; 88 } 89 } 90 } 91 92 Sudo[Y][X] = 0; 93 return false; 94 } 95 96 int main() 97 { 98 ifstream cin("input.txt"); 99 100 101 102 for (int i=0; i<9; i++) 103 { 104 vector<int> vecTmp; 105 for (int j=0; j<9; j++) 106 { 107 int nTmp; 108 109 cin >> nTmp; 110 vecTmp.push_back(nTmp); 111 } 112 Sudo.push_back(vecTmp); 113 vecTmp.clear(); 114 } 115 116 if(!DFS(0, 0)) 117 { 118 cout << "输入数据有误" << endl; 119 } 120 121 for (int i=0; i<9; i++) 122 { 123 for (int j=0; j<9; j++) 124 { 125 cout << Sudo[i][j] << " "; 126 } 127 cout << endl; 128 } 129 130 while (true) 131 { 132 133 } 134 135 return 0; 136 }
好,用上面提到的,在input.txt中输入下面的数独,测试一下。
我的能够成功,速度也还接受得了。
好了,下面来点有难度的了。
例如下面的数独:
这个数独,用上面的程序计算的话,那就不是一般的慢了。
所以,必须考虑优化算法。
那么该怎么优化呢?我想先听听大家的看法。
本文待续......
---恢复内容结束---
最近在一个数独网站玩数独游戏,网站地址为:http://www.sudokufans.org.cn/。
由于自己数独能力不是特别强,解题比较慢,但是自己是程序猿,所以,我想,自己写个数独计算器吧,让电脑帮我去算得了。
由于我是C程序猿,所以第一步要做的是,先不管界面,做一个黑底白字的win32控制台应用程序,用于验证自己的算法。
好了,开工,所以做一个简单的儿童4阶数独,如图:
,
我程序是这样使用的,首先,在程序的同目录下放一个input.txt文件用于输入,其中,未知数用0表示,每个数之间一个空格,例如上图的input.txt文件的内容为:
4 0 3 0
3 0 0 0
0 0 0 0
0 0 0 1
然后点击根据我代码生成的程序,就得到输出结果。
。
由于数据比较简单,比较才是4X4的数独,所以也没做什么优化,就是通过数据结构里图论中的DFS从第一个未知数开始,至上而下,从左到右依次枚举每种可能解。
代码如下:
1 #include <iostream> 2 #include <fstream> 3 #include <set> 4 #include <vector> 5 using namespace std; 6 7 //#define DEBUG 8 9 vector<vector<int> > Sudo; 10 11 void PrintSudo() 12 { 13 for (int i=0; i<4; i++) 14 { 15 for (int j=0; j<4; j++) 16 { 17 cout << Sudo[i][j] << " "; 18 } 19 cout << endl; 20 } 21 } 22 23 bool DFS(int X, int Y) 24 { 25 if (Y >= 4) 26 { 27 return true; 28 } 29 30 if (X >= 4) 31 { 32 return DFS(0, Y + 1); 33 } 34 35 if (Sudo[Y][X] != 0) 36 { 37 return DFS(X + 1, Y); 38 } 39 40 set<int> HaveExist; 41 int i, j; 42 43 for (i=0; i<4; i++) 44 { 45 if (Sudo[Y][i] != 0) 46 { 47 HaveExist.insert(Sudo[Y][i]); //同行中已存在的数 48 } 49 50 if (Sudo[i][X] != 0) 51 { 52 HaveExist.insert(Sudo[i][X]); //同列中已存在的数 53 } 54 } 55 56 for (i=Y/2*2; i<Y/2*2 + 2; i++) 57 { 58 for (j=X/2*2; j<X/2*2 + 2; j++) 59 { 60 if (Sudo[i][j] != 0) 61 { 62 HaveExist.insert(Sudo[i][j]); 63 } 64 } 65 } 66 67 68 for (i=1; i<=4; i++) 69 { 70 if (HaveExist.find(i) == HaveExist.end()) //数字i在当前数独还未存在,是候选数 71 { 72 Sudo[Y][X] = i; 73 #ifdef DEBUG 74 cout << "X=" << X << ", Y=" << Y << endl; 75 cout << "已存在的数:"; 76 77 for (set<int>::iterator it=HaveExist.begin(); it!=HaveExist.end(); it++) 78 { 79 cout << *it << " "; 80 } 81 cout << endl; 82 cout << "将Sudo[" << Y << "][" << X << "]设置成" << i << endl; 83 PrintSudo(); 84 #endif 85 if (DFS(X+1, Y)) 86 { 87 return true; 88 } 89 } 90 } 91 92 Sudo[Y][X] = 0; 93 return false; 94 } 95 96 int main() 97 { 98 ifstream cin("input.txt"); 99 100 101 102 for (int i=0; i<4; i++) 103 { 104 vector<int> vecTmp; 105 for (int j=0; j<4; j++) 106 { 107 int nTmp; 108 109 cin >> nTmp; 110 vecTmp.push_back(nTmp); 111 } 112 Sudo.push_back(vecTmp); 113 vecTmp.clear(); 114 } 115 116 if(!DFS(0, 0)) 117 { 118 cout << "输入数据有误" << endl; 119 } 120 121 for (int i=0; i<4; i++) 122 { 123 for (int j=0; j<4; j++) 124 { 125 cout << Sudo[i][j] << " "; 126 } 127 cout << endl; 128 } 129 130 while (true) 131 { 132 133 } 134 135 return 0; 136 }
好了,尝试了4X4的数独之后,再来尝试9X9的数独,首先,我们先简单的将之前的4改成9(当然,同区域的56和58的2改成3)看看情况会怎么样;
看代码
1 #include <iostream> 2 #include <fstream> 3 #include <set> 4 #include <vector> 5 using namespace std; 6 7 //#define DEBUG 8 9 vector<vector<int> > Sudo; 10 11 void PrintSudo() 12 { 13 for (int i=0; i<9; i++) 14 { 15 for (int j=0; j<9; j++) 16 { 17 cout << Sudo[i][j] << " "; 18 } 19 cout << endl; 20 } 21 } 22 23 bool DFS(int X, int Y) 24 { 25 if (Y >= 9) 26 { 27 return true; 28 } 29 30 if (X >= 9) 31 { 32 return DFS(0, Y + 1); 33 } 34 35 if (Sudo[Y][X] != 0) 36 { 37 return DFS(X + 1, Y); 38 } 39 40 set<int> HaveExist; 41 int i, j; 42 43 for (i=0; i<9; i++) 44 { 45 if (Sudo[Y][i] != 0) 46 { 47 HaveExist.insert(Sudo[Y][i]); //同行中已存在的数 48 } 49 50 if (Sudo[i][X] != 0) 51 { 52 HaveExist.insert(Sudo[i][X]); //同列中已存在的数 53 } 54 } 55 56 for (i=Y/3*3; i<Y/3*3 + 3; i++) 57 { 58 for (j=X/3*3; j<X/3*3 + 3; j++) 59 { 60 if (Sudo[i][j] != 0) 61 { 62 HaveExist.insert(Sudo[i][j]); 63 } 64 } 65 } 66 67 68 for (i=1; i<=9; i++) 69 { 70 if (HaveExist.find(i) == HaveExist.end()) //数字i在当前数独还未存在,是候选数 71 { 72 Sudo[Y][X] = i; 73 #ifdef DEBUG 74 cout << "X=" << X << ", Y=" << Y << endl; 75 cout << "已存在的数:"; 76 77 for (set<int>::iterator it=HaveExist.begin(); it!=HaveExist.end(); it++) 78 { 79 cout << *it << " "; 80 } 81 cout << endl; 82 cout << "将Sudo[" << Y << "][" << X << "]设置成" << i << endl; 83 PrintSudo(); 84 #endif 85 if (DFS(X+1, Y)) 86 { 87 return true; 88 } 89 } 90 } 91 92 Sudo[Y][X] = 0; 93 return false; 94 } 95 96 int main() 97 { 98 ifstream cin("input.txt"); 99 100 101 102 for (int i=0; i<9; i++) 103 { 104 vector<int> vecTmp; 105 for (int j=0; j<9; j++) 106 { 107 int nTmp; 108 109 cin >> nTmp; 110 vecTmp.push_back(nTmp); 111 } 112 Sudo.push_back(vecTmp); 113 vecTmp.clear(); 114 } 115 116 if(!DFS(0, 0)) 117 { 118 cout << "输入数据有误" << endl; 119 } 120 121 for (int i=0; i<9; i++) 122 { 123 for (int j=0; j<9; j++) 124 { 125 cout << Sudo[i][j] << " "; 126 } 127 cout << endl; 128 } 129 130 while (true) 131 { 132 133 } 134 135 return 0; 136 }
好,用上面提到的,在input.txt中输入下面的数独,测试一下。
我的能够成功,速度也还接受得了。
好了,下面来点有难度的了。
例如下面的数独:
这个数独,用上面的程序计算的话,那就不是一般的慢了。
所以,必须考虑优化算法。
那么该怎么优化呢?我想先听听大家的看法。
本文待续......
首先分析下为什么上面的程序解上图中的数独时会很慢,因为前面的程序是暴力枚举所以可能的情况,直到找到可行解为止,而这个数独的已知数只有17个,而未知数却有81-17=64个,我假设平均每个格子有4个可能解,那么人品不好的话,可能要尝试4的64次方,这个数大得太恐怖了,所以必须进行剪枝。
怎么剪枝呢?我利用的是人脑解数独的一些方法,为了方便描述,我将横排编号为A-I,竖排编号为1-9,这样左上角的坐标便是A1,右下角的坐标便是I9,我先假设每个格子都可以填1-9这九种可能的数字,然后根据已知数,不断删除每个格子的可能性数,例如上图中根据已知数,可能得到可能性表:
。
接下了,就是很重要的优化步骤了,根据我们解数独方法,我们可以知道,在右上区域,只有I1有可能值为5,该区域其他格子都没有成为5的可能,所以I1必为5(玩过数独的应该很容易理解)。确定I1为5后,又可以删除同行、同列其他格子5的可能情况:
。
同理,可以确定E5=6,C9=6,等等,因此程序的流程已经比较清晰,由于不会画流程图,所以只能先用文字描述程序流程,求会画流程图的大神提供帮助。
1.初始化数独的可能性表,让每个格子都有1-9这9种可能;
2.输入已知数,每输入一个已知数,便确定了一个值;
3.根据该确定值删除同行、同列、同区域中其他格子的该确定值的可能值;
4.在删除格子可能值的时,判断删除完后,该格子是否只剩唯一的可能值了,如果是,则说明又确定一个格子的值,执行步骤3;
5.输入完已知数后,判断每个格子包含的可能值是该行或该列或该区域其他格子的可能性表中没有的,则可确定该格的值便是这个特有的可能值,执行步骤3.
6.对于剩下的未知数,根据其可能性表做DFS,求得最终可行解。
代码如下:
1 #include <iostream> 2 #include <fstream> 3 #include <list> 4 #include <vector> 5 #include <algorithm> 6 using namespace std; 7 8 const int SUDOSIZE = 9; 9 10 //#define DEBUG 11 typedef vector<vector<list<int> > > SudoPoss_t; 12 SudoPoss_t g_SudoPoss; //数独每个位置可选择数字集合数组 13 14 inline int IntSqrt(int n); 15 //初始化可能性表 16 //一开始每个格子都有填1-9的可能 17 void Init(); 18 void ShowGridPossiNums(SudoPoss_t SudoPoss, const int X, const int Y); 19 bool AssumeOneValue(SudoPoss_t &SudoPoss, const int& X, const int& Y, const int& Value); 20 bool IsOnlyPossibleInSameRow(int X, int Y, int PossiVal); 21 bool IsOnlyPossibleInSameCol(int X, int Y, int PossiVal); 22 bool IsOnlyPossibleInSameArea(int X, int Y, int PossiVal); 23 //例如 24 //0 2 0 0 1 0 0 0 0 25 //0 0 0 0 0 4 0 8 3 26 //0 0 0 0 0 5 0 7 X 27 //0 0 0 0 0 8 0 0 0 28 //7 0 0 0 0 3 0 0 0 29 //0 9 0 0 0 0 1 0 0 30 //8 0 0 0 0 0 0 0 0 31 //0 0 0 0 2 0 6 0 0 32 //0 0 0 0 9 0 0 4 0 33 //只有X可以为1,因为该区域内只有X有为1的可能性,所以可以确定X为1,排除此处其他可能性 34 void ConfirmOnlyPossible(); 35 void ReadInput(); 36 void ShowAllPossNums(); 37 bool DFS(SudoPoss_t SudoPoss, int X, int Y); 38 39 int main() 40 { 41 42 43 Init(); 44 45 try 46 { 47 ReadInput(); 48 } 49 catch (int e) 50 { 51 cout << "输入数独数据错误" << endl; 52 return -1; 53 } 54 55 ConfirmOnlyPossible(); 56 57 if(!DFS(g_SudoPoss, 0, 0)) 58 { 59 cout << "输入数据有误" << endl; 60 } 61 62 for (int i=0; i<SUDOSIZE; i++) 63 { 64 for (int j=0; j<SUDOSIZE; j++) 65 { 66 cout << *g_SudoPoss[i][j].begin() << " "; 67 } 68 cout << endl; 69 } 70 71 while(true) 72 { 73 74 } 75 76 return 0; 77 } 78 79 inline int IntSqrt(int n) 80 { 81 if (1 == n || 0 == n) 82 { 83 return n; 84 } 85 86 for (int i = n / 2; i>=1; i--) 87 { 88 if (i * i == n) 89 { 90 return i; 91 } 92 } 93 return -1; 94 } 95 96 97 //初始化可能性表 98 //一开始每个格子都有填1-9的可能 99 void Init() 100 { 101 //set<int> setTmp; 102 list<int> listTmp; 103 104 for (int i=1; i<=9; i++) 105 { 106 listTmp.push_back(i); 107 } 108 109 vector<list<int> > vecTmp; 110 for (int j=0; j<SUDOSIZE; j++) 111 { 112 vecTmp.push_back(listTmp); 113 } 114 115 for (int i=0; i<SUDOSIZE; i++) 116 { 117 g_SudoPoss.push_back(vecTmp); 118 } 119 } 120 121 //显示第(X,Y)位置格子可供选择的数字 122 void ShowGridPossiNums(SudoPoss_t SudoPoss, const int X, const int Y) 123 { 124 cout << "SudoPoss[" << Y << "][" << X << "] :" ; 125 for (list<int>::iterator it=SudoPoss[Y][X].begin(); it!=SudoPoss[Y][X].end(); it++) 126 { 127 cout << " " << *it ; 128 } 129 cout << " size() = " << SudoPoss[Y][X].size(); 130 cout << endl; 131 } 132 133 //假设(X,Y)位置处确定为值Value,删除其他位置的Value值的可能情况,从而进行剪枝 134 bool AssumeOneValue(SudoPoss_t &SudoPoss, const int& X, const int& Y, const int& Value) 135 { 136 if (SudoPoss[Y][X].size() == 0) 137 { 138 return false; 139 } 140 141 //如果某个位置是已知数,则将该位置其他可能数删除 142 for (list<int>::iterator it=SudoPoss[Y][X].begin(); it!=SudoPoss[Y][X].end(); ) 143 { 144 if (*it != Value) 145 { 146 SudoPoss[Y][X].erase(it); 147 it=SudoPoss[Y][X].begin(); 148 continue; 149 } 150 151 it++; 152 } 153 154 //在同行中其他格子中删除该已知数 155 for (int i=0; i<SUDOSIZE; i++) 156 { 157 if (i == X) 158 { 159 continue; 160 } 161 162 list<int>::iterator it = find(SudoPoss[Y][i].begin(), SudoPoss[Y][i].end(), Value); 163 if (it != SudoPoss[Y][i].end()) 164 { 165 SudoPoss[Y][i].erase(it); 166 167 //如果某格没有任何可能的情况 则表示该推测错误 168 if (0 == SudoPoss[Y][i].size()) 169 { 170 return false; 171 } 172 //通过剪枝使某一个只有一种可能的情况,则针对该格继续剪枝 173 else if (1 == SudoPoss[Y][i].size()) 174 { 175 if (!AssumeOneValue(SudoPoss, i, Y, *SudoPoss[Y][i].begin())) 176 { 177 return false; 178 } 179 } 180 } 181 } 182 183 //在同列中其他格子删除该已知数 184 for (int i=0; i<SUDOSIZE; i++) 185 { 186 if (i == Y) 187 { 188 continue; 189 } 190 191 list<int>::iterator it = find(SudoPoss[i][X].begin(), SudoPoss[i][X].end(), Value); 192 if (it != SudoPoss[i][X].end()) 193 { 194 SudoPoss[i][X].erase(it); 195 196 if (0 == SudoPoss[i][X].size()) 197 { 198 return false; 199 } 200 else if (1 == SudoPoss[i][X].size()) 201 { 202 if (!AssumeOneValue(SudoPoss, X, i, *SudoPoss[i][X].begin())) 203 { 204 return false; 205 } 206 } 207 } 208 } 209 210 //在同区域中其他格子删除该已知数 211 for (int i=Y/IntSqrt(SUDOSIZE)*IntSqrt(SUDOSIZE); i<Y/IntSqrt(SUDOSIZE)*IntSqrt(SUDOSIZE) + IntSqrt(SUDOSIZE); i++) 212 { 213 for (int j=X/IntSqrt(SUDOSIZE)*IntSqrt(SUDOSIZE); j<X/IntSqrt(SUDOSIZE)*IntSqrt(SUDOSIZE) + IntSqrt(SUDOSIZE); j++) 214 { 215 if (i == Y && j == X) 216 { 217 continue; 218 } 219 220 list<int>::iterator it = find(SudoPoss[i][j].begin(), SudoPoss[i][j].end(), Value); 221 if (it != SudoPoss[i][j].end()) 222 { 223 SudoPoss[i][j].erase(it); 224 225 if (0 == SudoPoss[i][j].size()) 226 { 227 return false; 228 } 229 else if (1 == SudoPoss[i][j].size()) 230 { 231 if (!AssumeOneValue(SudoPoss, j, i, *SudoPoss[i][j].begin())) 232 { 233 return false; 234 } 235 } 236 } 237 } 238 } 239 return true; 240 } 241 242 bool IsOnlyPossibleInSameRow(int X, int Y, int PossiVal) 243 { 244 for (int i=0; i<SUDOSIZE; i++) 245 { 246 if (i == X) 247 { 248 continue; 249 } 250 251 if(find(g_SudoPoss[Y][i].begin(), g_SudoPoss[Y][i].end(), PossiVal) != g_SudoPoss[Y][i].end()) 252 { 253 return false; 254 } 255 } 256 return true; 257 } 258 259 bool IsOnlyPossibleInSameCol(int X, int Y, int PossiVal) 260 { 261 for (int i=0; i<SUDOSIZE; i++) 262 { 263 if (i == Y) 264 { 265 continue; 266 } 267 268 if(find(g_SudoPoss[i][X].begin(), g_SudoPoss[i][X].end(), PossiVal) != g_SudoPoss[i][X].end()) 269 { 270 return false; 271 } 272 } 273 return true; 274 } 275 276 bool IsOnlyPossibleInSameArea(int X, int Y, int PossiVal) 277 { 278 //在同区域中其他格子删除该已知数 279 for (int i=Y/IntSqrt(SUDOSIZE)*IntSqrt(SUDOSIZE); i<Y/IntSqrt(SUDOSIZE)*IntSqrt(SUDOSIZE) + IntSqrt(SUDOSIZE); i++) 280 { 281 for (int j=X/IntSqrt(SUDOSIZE)*IntSqrt(SUDOSIZE); j<X/IntSqrt(SUDOSIZE)*IntSqrt(SUDOSIZE) + IntSqrt(SUDOSIZE); j++) 282 { 283 if (i == Y && j == X) 284 { 285 continue; 286 } 287 288 if(find(g_SudoPoss[i][j].begin(), g_SudoPoss[i][j].end(), PossiVal) != g_SudoPoss[i][j].end()) 289 { 290 return false; 291 } 292 } 293 } 294 return true; 295 } 296 297 //例如 298 //0 2 0 0 1 0 0 0 0 299 //0 0 0 0 0 4 0 8 3 300 //0 0 0 0 0 5 0 7 X 301 //0 0 0 0 0 8 0 0 0 302 //7 0 0 0 0 3 0 0 0 303 //0 9 0 0 0 0 1 0 0 304 //8 0 0 0 0 0 0 0 0 305 //0 0 0 0 2 0 6 0 0 306 //0 0 0 0 9 0 0 4 0 307 //只有X可以为1,因为该区域内只有X有为1的可能性,所以可以确定X为1,排除此处其他可能性 308 void ConfirmOnlyPossible() 309 { 310 for (int i=0; i<SUDOSIZE; i++) 311 { 312 for (int j=0; j<SUDOSIZE; j++) 313 { 314 if (g_SudoPoss[i][j].size() == 1) 315 { 316 continue; 317 } 318 319 for (list<int>::iterator it=g_SudoPoss[i][j].begin(); it!=g_SudoPoss[i][j].end(); it++) 320 { 321 if (IsOnlyPossibleInSameArea(j, i, *it) 322 || IsOnlyPossibleInSameCol(j, i, *it) 323 || IsOnlyPossibleInSameRow(j, i, *it)) 324 { 325 // cout << "确定Sudo[" << i << "][" << j << "]为" << *it << endl; 326 AssumeOneValue(g_SudoPoss, j, i, *it); 327 //重新开始循环 328 i = -1; 329 j = SUDOSIZE; 330 break; 331 } 332 } 333 } 334 } 335 } 336 337 void ReadInput() 338 { 339 ifstream cin("input.txt"); 340 341 for (int i=0; i<SUDOSIZE; i++) 342 { 343 for (int j=0; j<SUDOSIZE; j++) 344 { 345 int nTmp; 346 347 cin >> nTmp; 348 349 if (0 == nTmp) 350 { 351 continue; 352 } 353 354 if (!AssumeOneValue(g_SudoPoss, j, i, nTmp)) 355 { 356 throw 0; 357 } 358 } 359 } 360 cin.close(); 361 } 362 void ShowAllPossNums() 363 { 364 for (int i=0; i<SUDOSIZE; i++) 365 { 366 for (int j=0; j<SUDOSIZE; j++) 367 { 368 ShowGridPossiNums(g_SudoPoss, j, i); 369 } 370 } 371 } 372 bool DFS(SudoPoss_t SudoPoss, int X, int Y) 373 { 374 if (Y >= SUDOSIZE) 375 { 376 g_SudoPoss = SudoPoss; 377 return true; 378 } 379 380 if (X >= SUDOSIZE) 381 { 382 return DFS(SudoPoss, 0, Y + 1); 383 } 384 385 if (SudoPoss[Y][X].size() == 1) 386 { 387 return DFS(SudoPoss, X + 1, Y); 388 } 389 390 for (list<int>::iterator it=SudoPoss[Y][X].begin(); it!=SudoPoss[Y][X].end(); it++) 391 { 392 SudoPoss_t TmpSudoPoss = SudoPoss; 393 394 if (!AssumeOneValue(TmpSudoPoss,X, Y, *it)) 395 { 396 continue; 397 } 398 if (!DFS(TmpSudoPoss, X + 1, Y)) 399 { 400 continue; 401 } 402 else 403 { 404 return true; 405 } 406 } 407 return false; 408 }
底层算法暂时到这里,欢迎大家继续提出优化建议,至于前端的界面,我目前也还正在学习win32GUI编程,等掌握好后,再做前端界面。
所以,本文待续......