---恢复内容开始---

最近在一个数独网站玩数独游戏,网站地址为: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编程,等掌握好后,再做前端界面。

所以,本文待续......

转载于:https://www.cnblogs.com/BTMaster/archive/2013/02/05/2892787.html

手把手教你写数独计算器(1)相关推荐

  1. 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫

    系列教程 手把手教你写电商爬虫-第一课 找个软柿子捏捏 如果没有看过第一课的朋友,请先移步第一课,第一课讲了一些基础性的东西,通过软柿子"切糕王子"这个电商网站好好的练了一次手,相 ...

  2. 手把手教你写一个生成对抗网络

    成对抗网络代码全解析, 详细代码解析(TensorFlow, numpy, matplotlib, scipy) 那么,什么是 GANs? 用 Ian Goodfellow 自己的话来说: " ...

  3. php注册程序,[PHP初级]手把手教你写注册程序 1

    [PHP初级]手把手教你写注册程序 1 实例内容 在此教程,我们将通过写一个用户注册程序,学习以下内容: 数据的传输与获取 信息的验证 pdo方式操作数据库 事务处理 前台显示文件:index.php ...

  4. python k线合成_手把手教你写一个Python版的K线合成函数

    手把手教你写一个Python版的K线合成函数 在编写.使用策略时,经常会使用一些不常用的K线周期数据.然而交易所.数据源又没有提供这些周期的数据.只能通过使用已有周期的数据进行合成.合成算法已经有一个 ...

  5. 手把手教你写Linux I2C设备驱动

    手把手教你写Linux I2C设备驱动 标签:Linux 设备 驱动 详解 i2c 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:/ ...

  6. 手把手教你写DI_2_小白徒手撸构造函数注入

    在上一节:手把手教你写DI_1_DI框架有什么? 我们已经知道我们要撸哪些东西了 那么我们开始动工吧,这里呢,我们找小白同学来表演下 小白同学 :我们先定义一下我们的广告招聘纸有什么: 好,我们实现两 ...

  7. 手把手教你写DI_3_小白徒手支持 Singleton 和 Scoped 生命周期

    在上一节:手把手教你写DI_2_小白徒手撸构造函数注入 浑身绷带的小白同学:我们继续开展我们的工作,大家都知道 Singleton是什么,就是全局只有一个呗,我们就先从它开始,这个多简单,我们找个字典 ...

  8. 手把手教你写一个spring IOC容器

    本文分享自华为云社区<手把手教你写一个spring IOC容器>,原文作者:技术火炬手. spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功 ...

  9. 手把手教你写网站:Python WEB开发技术实战

    摘要:本文详细介绍了Python WEB开发的基础入门.以一个博客站点的开发为例讲解了基于Django框架开发WEB站点的全过程.通过本文的学习可以快速掌握基于Django的Python WEB的开发 ...

  10. 手把手教你写专利申请书/如何申请专利

    摘要 小前言 (一)申请前的准备工作     1.申请前查询     2.其他方面的考虑     3.申请文件准备 (二)填写专利申请系列文档     1.实际操作步骤     2.具体操作     ...

最新文章

  1. 找java培训机构如何挑选
  2. elastic stack中的Beats是什么?
  3. WIN10 + VS2015 + WDK10 + SDK10 + VM虚拟机驱动开发调试环境搭建
  4. Spring+Redis整合
  5. python基本对象类型
  6. ubuntu16.04安装zabbix-server3.4
  7. 提取一个文件中的相同的文件类型
  8. jdk 中文开发文档
  9. 微pe添加网络组件_为微PE添加网络组件win10 64位纯净维护系统集合常见网卡驱动支持BIOS/UEFI量产NVME...
  10. idea打包时控制台中文乱码
  11. BadBoy下载安装
  12. CMD 下载并运行软件 Cmd Bat Powershell
  13. 图像加密标准测试图库
  14. 手把手教你使用QGIS制作地图
  15. 凯立德地图导航2022年懒人包 安卓版
  16. 用计算机播放cd,电脑上怎么放光盘_播放CD的方法步骤
  17. 从前,有座山,山里有座庙,庙里有苦逼IT
  18. 【Unity Shader】 CubeMap(立方体贴图)
  19. 语音识别之语音特征提取一
  20. python贴吧自动签到,解放你的双手

热门文章

  1. 人工智能-机器学习:对抗攻击与防御(Adversarial Attack and Defense)
  2. 著名建筑师马岩松元宇宙首作落地百度希壤 迪奥元宇宙首展同期揭幕
  3. Aspose.Words五 MergeField
  4. Prometheus(四)——Alertmanager
  5. 华为选用ServiceHot ITSM提升运维服务能力
  6. 只需操作这三步,轻松找回电脑上被删除的文件
  7. 华为手机怎么测试Android,华为手机怎么对硬件进行检测?华为手机硬件检测教程...
  8. 要运行python程序要安装什么_求你要我(2)
  9. JAVA后台开发提升注解篇 @Deprecated
  10. python中的join是什么意思_python里join是什么意思