目录

一,不规则数独

1,规则

2,对称性

含正方形的不规则数独

中心对称的不规则数独

其他对称不规则数独

3,计算机求解

二,变种数独

1,规则

2,对角线数独

3,窗口数独

4,超级窗口数独

5,奇偶数独

情人节趣味数独

6,摆造型的数独

年字数独

京字数独

7,连续数独

8,连续数独2

9,箭头数独

10,边框数独

边框奇偶数独

摩天楼数独

11,满覆盖杀手数独

计算机求解V1

计算机求解V2

计算机求解V3

12,非满覆盖杀手数独

计算机求解V1

计算机求解V2

13,倍数数独

14,不等式数独

15,武士数独

16,变种数独的规则退化现象


一,不规则数独

在线play

1,规则

没有分9个宫,而是用特定的线分成9个区,其他规则和标准数独一样。

2,对称性

在我做过的这些不规则数独里面,居然全部有着对称性!

有的是格子有对称性,有的是数字有对称性,还有的是格子和数字都有对称性。

对称性又分很多很多种。

含正方形的不规则数独

这5个不规则数独,正中间都是正方形,而且里面给出的数字都是对称分布的。

具体说来

图一的格子是中心对称的,数字几乎是中心对称的。

图二的格子和数字都是左右对称的

下面一行的三个图,格子和数字都是中心对称的

这个图有2个正方形,也是格子和数字都是中心对称的。

这个图有3个正方形,格子是关于主对角线对称的,而数字是关于2条对角线都对称的。

中心对称的不规则数独

不规则数独有很多很多都是格子成中心对称的,比如下面这个:

然而更可怕的是,有的数独在此基础上,数字的位置也有着规律。

这3个数独,数字也是成中心对称的

   

这2个数独还是挺像的,而且,数字的位置,都是关于2个对角线对称的。

关于2个对角线对称是中心对称里面的一种,比一般的中心对称更具有对称美。

   

这3个数独,数字都是90°旋转对称。

90°旋转对称是中心对称里面的一种,比一般的中心对称更具有对称美。

    

这3个数独,一看就是非常非常对称,上下对称,左右对称,对角线对称,90度旋转对称

其他对称不规则数独

格子是关于副对角线对称的,而数字是上下对称而且左右对称

格子是左右对称的

格子是左右对称的,而数字是关于副对角线对称的。

格子是关于副对角线对称的,而数字是左右对称的。

3,计算机求解

前面我给出了用于求解任何规则数独的代码,这里我将对代码进行修改,得到可以用于求解任何不规则数独的代码。

不规则数独和规则数独的区别,在于划分9个区的方式不同,规则数独是9个3*3的正方形区,而不规则数独的分区各种各样的。

只需要用1个数组par记录如何分区,即可求解不规则数独。

代码:

#include<iostream>
using namespace std;int list[10][10];
int par[10][10];        //par[i][j]=n
int anti[10][10];       //anti[n][?]=i*10+j;void init()
{cout << "输入数字,没有数字的用0代替" << endl;char c;for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++){cin >> c;list[i][j] = c - '0';}cout << "输入每个格子属于哪一块(1-9)" << endl;for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++)anti[i][j] = 0;for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++){cin >> c;par[i][j] = c - '0';for (int k = 1; k < 10; k++)if (anti[par[i][j]][k] == 0){anti[par[i][j]][k] = i * 10 + j;break;}}
}bool ok(int i, int j, int k)
{for (int jj = 1; jj < 10; jj++)if (list[i][jj] == k)return false;for (int ii = 1; ii < 10; ii++)if (list[ii][j] == k)return false;int p = par[i][j];for (int ii = 1; ii < 10; ii++)if (list[anti[p][ii] / 10][anti[p][ii] % 10] == k)return false;return true;
}bool trys(int i, int j)
{if (j == 10){i++;j = 1;}if (i == 10)return true;if (list[i][j])return trys(i, j + 1);for (int k = 1; k < 10; k++){if (ok(i, j, k)){list[i][j] = k;if (trys(i, j + 1))return true;}}list[i][j] = 0;return false;
}void out()
{for (int i = 1; i < 10; i++){for (int j = 1; j < 9; j++)cout << list[i][j] << " ";cout << list[i][9] << endl;}
}int main()
{init();trys(1, 1);out();system("pause>nul");return 0;
}

在ok函数中,由任何一个格子的坐标,需要直接得到其他所有格子的坐标,所以需要一个数组anti将par反过来记录。

其实不用这个数组也行,大不了在ok里面做一个搜索,搜索81个格子,根据par值判断是否在一个区。

这样做,代码稍微简单一点点,虽然复杂度是一样的,但是时间肯定慢一些。

上面的代码,对于任何不规则数独,应该都能在2秒之内输出答案。

用法示例:

对于这个数独,将空格全部补0:

030159080
209000603
007803400
900040005
706000108
300090006
002907500
501000802
070516020

然后,将9个区编号1-9:

然后同样的变成9*9的方阵:

122333444
122333444
122233444
125253666
115555566
111859596
777889996
777888996
777888996

将这2个9*9的方阵输入程序即可得到答案:

所以答案是:

二,变种数独

1,规则

变种数独是在规则数独的基础之上,加一点点特殊的规则。

这些规则,有的也可以加在不规则数独之上,不过比较少见。

因为多了一些约束,所以相对来说,一般来说给出的数字比较少。

变种数独里面,有很多都是规定一些大小为9的区域,这些区域里面都是1到9的数字。

2,对角线数独

在规则数独的基础上,还要满足2条对角线也是1到9

比如这个:

除了很对称之外,好像没什么特点。

但是,这个数独很特殊,要自己去解才能体会。

在解这个数独的时候,你会感觉,这个数独几乎有一套独特的推理规则,

规则数独常用的方法在这里很不好用。

规则数独,用行唯一性、列唯一性是最多的。

而这个数独,行唯一性、列唯一性用的很少,反而对角线用的很多很多!

然而,比对角线用的更多的,是一个奇特的规律,仅对本数独有效!

这个规律我不明说,在下面的答案我用图画出来

对角线的规则可以叠加在其他变种数独之上,比如窗口数独,

也可以叠加在不规则数独之上。

3个奇特的数独:

    

这3个数独,一看就都有着较强的对称性。

然而,不仅是对称性,任何2个数字之间的曼哈顿距离都是偶数!

也就是说,如果把数独按照如下方式染色的话

这3个数独的所有数字都会被黑格子盖住!

他们的解分别是:

    

第一个数独很难,第二个数独难度普通,第三个数独很简单。

他们给出的初始数字分别有24、28、24个,但是因为结构差异很大,所以难度差别大。

这3个数独,共性很明显,却又都有着很独特的结构,解法截然不同,

而且都和普通的规则数独解法不太像,这些,只有自己去解才能体会的到!

3,窗口数独

在规则数独的基础上,还要满足4个灰色的宫也是1到9

答案:

4,超级窗口数独

在规则数独的基础上,还有9个块也都是1到9

约束比较多,答案:

5,奇偶数独

变种数独里面,还有各种奇偶数独,就是标出特定的区域必须是奇数或者必须是偶数。

情人节趣味数独

情人节趣味数独这个是奇偶数独的一种。

这个趣味数独的规则和规则数独差不多,只是标出了16个格子必须是偶数。

求解的过程也很有趣:

到这个时候,奇数全部填完了,偶数还没开始填(除了最开始本来就给了的)

不是我刻意而为之,而是按照正常的思路来求解,就会出现这一幕。

接下来就十分简单了。

实际上,到了这一步,规则就已经退化了。

因为奇数已经填完了,所以剩下的自然全部是偶数,所以现在这个数独的规则已经和规则数独一样了。

6,摆造型的数独

年字数独

这个就是对角线数独了。

对于对角线数独,一般来说,对角线是最好填的。

这很好理解,因为对角线的格子所拥有的约束较多,所以更容易确定数字。

到了这个时候,对角线上的数字已经全部给出,现在这个数独已经和规则数独没有区别了。

京字数独

这个数独,很明显不可能是对角线数独,因为副对角线上面已经也2个2了。

所以图片中写的是错的,可能是数字写错了,所以这个只能按照规则数独去解。

答案:

7,连续数独

连续数独是,在规则数独的基础上,多了一个规则:

相邻两格内数字之差为1的两格间标出粗线,相邻两格间若没有粗线则两数字之差不能为1。

求解很容易。

8,连续数独2

在线play

去掉九宫格,即使是9*9的也没有宫。

4*4

  

9*9

9,箭头数独

这个数独和杀手数独有一定的类似性,但是难度自然比不了杀手数独。

相似性在于,突破点都是枚举拆分的组合,如果能排除掉其他的,得到唯一的组合,就能得到一些信息。

这样,箭头路径上的格子是最先破解的:

到了这个时候,这个数独就和规则数独没有差别了,这就是规则退化现象

最后的结果:

10,边框数独

边框类数独,每行的左边右边都有一个数,每列的上边下边都有一个数,用来描述一些信息。

边框奇偶数独

边框奇偶数独的规则是,在规则数独的基础上,对每一行和每一列都有2个约束。

对于列来说,如果上面写的是奇数,那么最上面的奇数必须是这个数,如果上面写的是偶数,那么最上面的偶数必须是这个数,每一列的下面同理。

行和列同理。

比如说,上面的数独里面,第一列最上面的奇数必须是3,最下面的奇数必须是9,

第一行的最左边的偶数必须是8,最右边的奇数必须是9。

边框奇偶数独是我本人最喜欢的数独之一,中学的时候做过很多,不过都没有留下记载。

边框奇偶数独非常有趣,一般情况下,初始给出的数字很少,上面的数独里面只给出了7个。

她有趣的地方体现在,很容易做错!

之所以把边上的数字都抹掉了,是因为,逐步的记录规则的退化,有利于化简。

整个求解的过程我都录制成了视频:资源分享汇总_nameofcsdn的博客-CSDN博客

摩天楼数独

在线play

在标准规则的基础上,四周数字代表楼房的高度,高楼会挡住低楼(也就是大数字挡住小数字,小数字就是看不到的了),周围的数字是从这个角度可以看到的楼房的数目。

求解:

       

11,满覆盖杀手数独

在线play

满覆盖杀手数独是不给出数字的。

满覆盖杀手数独是在规则数独的基础上,增加2条规则:

第一,每个虚线框内所有数字都不同

第二,每个虚线框内所有数字之和都为虚线框所标注的数。

在资源分享汇总_nameofcsdn的博客-CSDN博客有我求解这个数独的时候录的完整的视频

计算机求解V1

满覆盖杀手数独也是有9个3*3的宫,这一点是和规则数独一样的。

而对于每一个小的区,给出了这个区中所有数字之和,这一点自然是必须要计算的。

这些区的划分没有固定的规律,只能逐一输入,根据区的编号标识,这一点,倒是和不规则数独一模一样。

下面先看代码再介绍如何使用。

代码:

#include<iostream>
using namespace std;int list__[10][10];
int par[10][10];        //par[i][j]=n , n is less than 40
int anti[40][10];       //anti[n][?]=i*10+j;
int sum[40];void init()
{for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++)list__[i][j] = 0;cout << "输入每一块的所有数字之和,以0结束" << endl;for (int i = 1; i < 40; i++){cin >> sum[i];if (sum[i] == 0)break;}cout << "输入每个格子属于哪一块" << endl;for (int i = 1; i < 40; i++)for (int j = 1; j < 10; j++)anti[i][j] = 0;for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++){cin >> par[i][j];for (int k = 1; k < 10; k++)if (anti[par[i][j]][k] == 0){anti[par[i][j]][k] = i * 10 + j;break;}}
}bool ok(int i, int j, int k)
{int p = par[i][j], m;if (k > sum[p])return false;if (k < sum[p]){bool flag = true;for (int ii = 1; ii < 10; ii++){m = anti[p][ii];if (m == 0)break;if (m == i * 10 + j)continue;if (list__[m / 10][m % 10] == 0)flag = false;if (list__[m / 10][m % 10] == k)return false;}if (flag)return false;}for (int jj = 1; jj < 10; jj++)if (list__[i][jj] == k)return false;for (int ii = 1; ii < 10; ii++)if (list__[ii][j] == k)return false;int x = (i - 1) / 3 * 3, y = (j - 1) / 3 * 3;for (int ii = x + 1; ii <= x + 3; ii++)for (int jj = y + 1; jj <= y + 3; jj++)if (list__[ii][jj] == k)return false;return true;
}bool trys(int i, int j)
{if (j == 10)i++, j = 1;if (i == 10)return true;if (list__[i][j])return trys(i, j + 1);for (int k = 1; k < 10; k++)if (ok(i, j, k)){list__[i][j] = k;sum[par[i][j]] -= k;if (trys(i, j + 1))return true;sum[par[i][j]] += k;}list__[i][j] = 0;return false;
}void out()
{for (int i = 1; i < 10; i++){cout << endl;for (int j = 1; j < 10; j++)cout << list__[i][j];}
}int main()
{init();auto s1=clock();trys(1, 1);out();auto e1 = clock();cout << endl<<e1 - s1;return 0;
}

使用示例:

首先,把这些区编号,顺序任意。

然后,各个区的左上角的数字按编号顺序写出来就是:(最后补1个0)

15 24 11 17 5 14 10 16 10 15 10 9 10 21 7 20 15 15 12 17 8 16 18 10 13 12 13 32 10 0

然后,把每个格子属于哪个区表示出来:

1  2  3  3  5  5  8  8  8
 1  2  2  4  6  7  7  9  9
 1  2  2  4  6  6 14 15  9
10 10 12 13 13 14 14 15 16
11 11 12 19 18 17 17 16 16
20 19 19 19 18 22 17 16 23
20 20 21 21 22 22 22 23 23
24 25 25 25 25 28 28 28 28
24 26 26 27 27 27 29 29 28

得到这2个数组,就可以开始求解了。

实际上,这2个数组已经包含了整个满覆盖杀手数独的所有信息。

这个和答案是一致的。

答案:

因为初始没有确定的数字,然后剪枝做的很粗略,所以求解整个满覆盖杀手数独大约需要300毫秒。

计算机求解V2

用状态压缩加快搜索


int list__[10][10];
int par[10][10];        //par[i][j]=n , n is less than 40
int anti[40][10];       //anti[n][?]=i*10+j;
int sum[40];
int row[10];
int col[10];
int gong[9];void init()
{for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++)list__[i][j] = 0;cout << "输入每一块的所有数字之和,以0结束" << endl;for (int i = 1; i < 40; i++){Read(sum[i]);if (sum[i] == 0)break;}cout << "输入每个格子属于哪一块" << endl;for (int i = 1; i < 40; i++)for (int j = 1; j < 10; j++)anti[i][j] = 0;for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++){Read(par[i][j]);if (par[i][j] == -1)continue;for (int k = 1; k < 10; k++) {if (anti[par[i][j]][k] == 0){anti[par[i][j]][k] = i * 10 + j;break;}}}
}bool ok(int i, int j, int k)
{int p = par[i][j], m;if (p > 0) {if (k > sum[p])return false;if (k < sum[p]){bool flag = true;for (int ii = 1; ii < 10; ii++){m = anti[p][ii];if (m == 0)break;if (m == i * 10 + j)continue;if (list__[m / 10][m % 10] == 0)flag = false;if (list__[m / 10][m % 10] == k)return false;}if (flag)return false;}}if (row[i] & (1 << k))return false;if (col[j] & (1 << k))return false;int x = (i - 1) / 3, y = (j - 1) / 3;if (gong[x * 3 + y] & (1 << k))return false;return true;
}void out()
{for (int i = 1; i < 10; i++){cout << endl;for (int j = 1; j < 10; j++)cout << list__[i][j];}
}bool trys(int id)
{if (id >= 81) {return true;}int i = id / 9 + 1, j = id % 9 + 1;if (list__[i][j])return trys(id+1);for (int k = 1; k < 10; k++)if (ok(i, j, k)){list__[i][j] = k;int x = (i - 1) / 3, y = (j - 1) / 3;row[i] ^= (1 << k);col[j] ^= (1 << k);gong[x * 3 + y] ^= (1 << k);sum[par[i][j]] -= k;if (trys(id + 1))return true;row[i] ^= (1 << k);col[j] ^= (1 << k);gong[x * 3 + y] ^= (1 << k);sum[par[i][j]] += k;}list__[i][j] = 0;return false;
}int main()
{init();auto s1 = clock();trys(0);out();auto e1 = clock();cout << endl << e1 - s1;return 0;
}

还是上面这个数独,一样的输入,运行时间从300毫秒优化到150毫秒。

计算机求解V3

如果能调整搜索顺序,而不是81个格子按顺序搜索,肯定能加速搜索。

我先试一下手动输入搜索顺序的效果:


int list__[10][10];
int par[10][10];        //par[i][j]=n 每个格子属于哪一块
vector<int>anti[82];    //每个分组有哪些格子
int antiLast[82];
int sum[82];
vector<int>tryId(81);
int row[10];
int col[10];
int gong[9];void init()
{for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++)list__[i][j] = 0;cout << "输入每一块的所有数字之和,以0结束" << endl;for (int i = 1; i < 40; i++){Read(sum[i]);if (sum[i] == 0)break;}cout << "输入每个格子属于哪一块" << endl;for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++){Read(par[i][j]);if (par[i][j] == -1)continue;anti[par[i][j]].push_back(i * 10 + j);antiLast[par[i][j]] = i * 10 + j;}
}bool ok(int i, int j, int k)
{int p = par[i][j];if (p > 0) {if (k > sum[p])return false;if (k < sum[p]){if (i * 10 + j == antiLast[p])return false;for (auto m : anti[p]){if (m == i * 10 + j)continue;if (list__[m / 10][m % 10] == k)return false;}}else {if( i * 10 + j != antiLast[p])return false;}}if (row[i] & (1 << k))return false;if (col[j] & (1 << k))return false;int x = (i - 1) / 3, y = (j - 1) / 3;if (gong[x * 3 + y] & (1 << k))return false;return true;
}void out()
{for (int i = 1; i < 10; i++){cout << endl;for (int j = 1; j < 10; j++)cout << list__[i][j];}
}bool trys(int id)
{if (id >= 81) {return true;}int i = tryId[id] / 9 + 1, j = tryId[id] % 9 + 1;if (list__[i][j])return trys(id + 1);for (int k = 1; k < 10; k++)if (ok(i, j, k)){list__[i][j] = k;int x = (i - 1) / 3, y = (j - 1) / 3;row[i] ^= (1 << k);col[j] ^= (1 << k);gong[x * 3 + y] ^= (1 << k);sum[par[i][j]] -= k;if (trys(id + 1))return true;row[i] ^= (1 << k);col[j] ^= (1 << k);gong[x * 3 + y] ^= (1 << k);sum[par[i][j]] += k;}list__[i][j] = 0;return false;
}void getTryId()
{int x;for (int i = 0; i < 81; i++) {cin >> x;tryId[x - 1] = i;}
}int main()
{init();getTryId();auto s1 = clock();  trys(0);out();auto e1 = clock();cout << endl << e1 - s1;return 0;
}

还是上面的数独,我大概编排了一下搜索顺序(需要保证每个小框的最后一行中最右边的格子是小框里面最后一个搜索的)

在搜索算法模板里面的代码是没有这个限制的,搜索顺序可以完全自定义。

输入:

15 24 11 17 5 14 10 16 10 15 10 9 10 21 7 20 15 15 12 17 8 16 18 10 13 12 13 32 10 0
 1  2  3  3  5  5  8  8  8
 1  2  2  4  6  7  7  9  9
 1  2  2  4  6  6 14 15  9
10 10 12 13 13 14 14 15 16
11 11 12 19 18 17 17 16 16
20 19 19 19 18 22 17 16 23
20 20 21 21 22 22 22 23 23
24 25 25 25 25 28 28 28 28
24 26 26 27 27 27 29 29 28
12    13    1    2    3    4    5    6    7
14    22    23    10    16    8    9    19    20
15    24    25    11    17    18    27    26    21
29    30    33    35    36    37    38    28    49
31    32    34    39    44    46    47    50    51
43    40    41    42    45    57    48    52    58
53    54    55    56    59    60    61    62    63
64    66    68    70    72    74    76    79    80
65    67    69    71    73    75    77    78    81

运行时间优化到15毫秒。

12,非满覆盖杀手数独

满覆盖杀手数独的小框是覆盖所有格子的。

非满覆盖数独就是小框数量少一点,只覆盖一部分格子,剩下的格子虽然总和也是知道的,但是不保证里面没有重复数字。(如果剩下的格子数量超过9则一定有重复数字)

这个非满覆盖杀手数独有的小框覆盖了53个格子,剩下28个格子里面显然有重复数字。

计算机求解V1

在枚举完小框之后,数独就退化成标准数独,则可以用dancing links去求解了。


class DancingLink // 精确覆盖算法
{
public:DancingLink(int m, int n, int maxNum) //01矩阵的行、列、1的最大数量{this->m = m, this->n = n, maxNum += n + 1;rhead.resize(m + 1), nums.resize(n + 1);row.resize(maxNum), col.resize(maxNum);up.resize(maxNum), down.resize(maxNum), lef.resize(maxNum), rig.resize(maxNum);sc.resize(m), rows.resize(m);for (int i = 0; i <= n; i++){up[i] = i, down[i] = i;lef[i] = i - 1, rig[i] = i + 1;row[i] = 0, col[i] = i, nums[i] = 0;}lef[0] = n, rig[n] = 0, nums[0] = INT_MAX;key = n;for (int i = 0; i <= m; i++)rhead[i] = 0;}void push(int r, int c)//新增坐标在(r,c)的一个节点{row[++key] = r, col[key] = c;up[key] = c, down[key] = down[c];up[down[c]] = key, down[c] = key;if (rhead[r] == 0)rhead[r] = lef[key] = rig[key] = key;else{lef[key] = rhead[r], rig[key] = rig[rhead[r]];lef[rig[rhead[r]]] = key, rig[rhead[r]] = key;}nums[c]++;}vector<vector<int>> getAllAns(){return dfs(false);}vector<int> getAnyAns(){auto v = dfs(true);if (v.size())return v[0];return vector<int>{};}
private:vector<vector<int>> dfs(bool onlyOne){vector<vector<int>>ans;while (true) {if (rig[0] == 0) {rows.resize(rowsid);ans.push_back(rows);rows.resize(m);if (onlyOne)return ans;}int c = min_element(nums.begin() + 1, nums.end()) - nums.begin();if (rig[0] == 0)c = 0;del(c);while (true) {c = down[c];if (c > n)break;reback(col[c]);if (scid == 0)return ans;c = sc[--scid];rowsid--;for (int j = rig[c]; j != c; j = rig[j])reback(col[j]);}sc[scid++] = c;//记录选中idrows[rowsid++] = row[c];for (int j = rig[c]; j != c; j = rig[j])del(col[j]);}return ans;}inline void del(int c)//删除第c列的所有元素和他们所在行的所有元素{lef[rig[c]] = lef[c], rig[lef[c]] = rig[c];for (int i = down[c]; i != c; i = down[i])for (int j = rig[i]; j != i; j = rig[j])down[up[j]] = down[j], up[down[j]] = up[j], nums[col[j]]--;nums[c] = INT_MAX;}inline void reback(int c)//完全回退del操作{lef[rig[c]] = rig[lef[c]] = c, nums[c] = 0;for (int i = down[c]; i != c; i = down[i]) {for (int j = rig[i]; j != i; j = rig[j])down[up[j]] = up[down[j]] = j, nums[col[j]]++;nums[c]++;}}
private:int m, n, key;vector<int>row, col;//每个节点的行,列vector<int>rhead;//每行第一个节点的idvector<int>up, down, lef, rig;//每个节点上下左右的节点idvector<int>nums;//每一列的元素个数vector<int>sc;int scid = 0, rowsid = 0;vector<int>rows;//覆盖选中的行,值的范围是从1到m
};string Sudoku(string s, int cEmpty = '.')
{int num = 0;for (auto c : s)if (c != cEmpty)num++;int m = (81 - num) * 9 + num;int n = 81 * 4;DancingLink d(m, n, m * 4 + n + 5);int row = 0;map<int, int>mrow;mrow[0] = -1;for (int i = 0; i < 81; i++) {//第i个格子char c = s[i];int low = 0, high = 8;if (c != cEmpty)low = high = c - '1';//第i个格子的搜索值域for (int x = low; x <= high; x++) {d.push(++row, i + 1), d.push(row, i / 9 * 9 + x + 81 + 1);d.push(row, i % 9 * 9 + x + 162 + 1), d.push(row, (i / 27 * 3 + i % 9 / 3) * 9 + x + 243 + 1);mrow[row] = i;}}vector<int> vans = d.getAnyAns();if (vans.empty())return "";string ans = s;for(auto row: vans){int id = mrow[row];if (s[id] == cEmpty) {ans[id] = '1';while (mrow[row - 1] == id)ans[id]++, row--;}else ans[id] = s[id];}return ans;
}int list__[10][10];
int par[10][10];        //par[i][j]=n 每个格子属于哪一块
vector<int>anti[82];    //每个分组有哪些格子
int antiLast[82];
int sum[82];
vector<int>tryId(81);
int row[10];
int col[10];
int gong[9];void init()
{for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++)list__[i][j] = 0;cout << "输入每一块的所有数字之和,以0结束" << endl;for (int i = 1; i < 40; i++){Read(sum[i]);if (sum[i] == 0)break;}cout << "输入每个格子属于哪一块" << endl;for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++){Read(par[i][j]);if (par[i][j] == -1)continue;anti[par[i][j]].push_back(i * 10 + j);antiLast[par[i][j]] = i * 10 + j;}
}bool ok(int i, int j, int k)
{int p = par[i][j];if (p > 0) {if (k > sum[p])return false;if (k < sum[p]){if (i * 10 + j == antiLast[p])return false;for (auto m : anti[p]){if (m == i * 10 + j)continue;if (list__[m / 10][m % 10] == k)return false;}}else {if( i * 10 + j != antiLast[p])return false;}}if (row[i] & (1 << k))return false;if (col[j] & (1 << k))return false;int x = (i - 1) / 3, y = (j - 1) / 3;if (gong[x * 3 + y] & (1 << k))return false;return true;
}void out()
{for (int i = 1; i < 10; i++){cout << endl;for (int j = 1; j < 10; j++)cout << list__[i][j];}
}int xxx = 0;
bool trys(int id)
{if (id == 59) {xxx++;if (xxx % 10000 == 0)cout << xxx << "  ";string s;char c = '0';for (int i = 1; i <= 9; i++)for (int j = 1; j <= 9; j++) {s += list__[i][j] ? list__[i][j] + c : '.';}s = Sudoku(s);if (s != "")cout << s << endl;return false;}if (id >= 81) {return true;}int i = tryId[id] / 9 + 1, j = tryId[id] % 9 + 1;if (list__[i][j])return trys(id + 1);for (int k = 1; k < 10; k++)if (ok(i, j, k)){list__[i][j] = k;int x = (i - 1) / 3, y = (j - 1) / 3;row[i] ^= (1 << k);col[j] ^= (1 << k);gong[x * 3 + y] ^= (1 << k);sum[par[i][j]] -= k;if (trys(id + 1))return true;row[i] ^= (1 << k);col[j] ^= (1 << k);gong[x * 3 + y] ^= (1 << k);sum[par[i][j]] += k;}list__[i][j] = 0;return false;
}void getTryId()
{int x;for (int i = 0; i < 81; i++) {cin >> x;tryId[x - 1] = i;}
}int main()
{init();getTryId();auto s1 = clock();  trys(0);out();auto e1 = clock();cout << endl << e1 - s1;return 0;
}

输入:

20    25    22    23    17    28    21    14    29    17    28    21    18    0
-1    -1    -1    -1    -1    -1    10    10    10
-1    8    8    8    -1    9    11    11    10
-1    -1    7    7    -1    9    11    11    10
5    6    6    7    -1    9    9    9    -1
5    5    5    -1    -1    -1    -1    -1    -1
5    6    6    -1    -1    12    12    13    -1
2    1    1    4    3    4    12    13    -1
2    2    1    4    3    4    -1    13    -1
2    2    2    3    3    3    -1    -1    -1

60    26    23    63    64    65    50    51    52
61    24    22    25    66    55    46    47    53
62    27    19    20    67    56    48    49    54
14    10    11    21    68    57    58    59    74
15    16    17    69    70    71    75    76    77
18    12    13    72    73    37    38    40    78
5    2    3    28    32    30    39    41    43
6    1    4    29    33    31    44    42    45
7    8    9    34    35    36    79    80    81

可惜计算量还是太大了,没算出来结果。

计算机求解V2

对于接近满覆盖的杀手数独,就不需要dancing links了,直接求解


bool trys(int id)
{if (id >= 81) {return true;}int i = tryId[id] / 9 + 1, j = tryId[id] % 9 + 1;if (list__[i][j])return trys(id + 1);for (int k = 1; k < 10; k++)if (ok(i, j, k)){list__[i][j] = k;int x = (i - 1) / 3, y = (j - 1) / 3;row[i] ^= (1 << k);col[j] ^= (1 << k);gong[x * 3 + y] ^= (1 << k);sum[par[i][j]] -= k;if (trys(id + 1))return true;row[i] ^= (1 << k);col[j] ^= (1 << k);gong[x * 3 + y] ^= (1 << k);sum[par[i][j]] += k;}list__[i][j] = 0;return false;
}void getTryId()
{int x;for (int i = 0; i < 81; i++) {cin >> x;tryId[x - 1] = i;}
}int main()
{init();getTryId();auto s1 = clock();trys(0);out();auto e1 = clock();cout << endl << e1 - s1;return 0;
}

输入:

26    14  16  12  16   6  14  14   12  8  20  12  26  22 6   6  38  16  14   6 14  0
-1     1     2     2      3      3    -1    4   -1
1      1      1    5      5     3    -1    4   4
6      1    -1    5      5     8    8     -1   7
6     9     9    -1     10    10    11   12   7
13   13    13    14    -1    10    11   12   12
15   13   13    14    14    -1    11   11   21
15    -1    16    16    17    17    -1   -1   21
18    -1    -1    17    17   17    -1   19   19
18    18    -1    17    17   20    20   19   -1
18    19    25    26    30    31    47    42    46
17    20    21    35    36    32    45    43    44
1    22    27    37    38    33    34    39    40
2    7    8    51    48    49    52    53    41
9    10    11    54    57    50    61    59    60
3    12    13    55    56    58    62    63    64
4    23    5    6    75    76    73    70    65
14    24    28    78    79    77    74    68    66
15    16    29    80    81    71    72    69    67

输出:

956834217
742169583
183725946
539241768
867953124
214678395
421587639
395416872
678392451

耗时72毫秒

13,倍数数独

把所有相邻格子的数是倍数关系的都标注出来的数独(1和其他数之间不算)

原始数独:

首先画出倍数关系:

然后画出简化图:

然后就可以开始求解数独了

比如,第8行第8列的格子是和3个不同的数都有倍数关系,那它就是2

再比如,第2列是一个长为6的串,所以一定是936248或者936284

于是得到:

照这个思路继续求解即可。

14,不等式数独

在线play

数字需要符合版面上不等式(大于/小于)要求。

4*4

  

5*5

   

7*7

15,武士数独

武士数独是5个数独叠在一起。

如果我没看错的话,这个武士数独,数字的位置居然是中心对称的!

首先,很明显,有4个特殊的3*3的小正方形是最容易破解的。

也就是最中间的标准数独和另外4个标准数独分别重合的小正方形。

这4个小正方形受到双方约束的叠加,所以很容易求出来。

于是第一步,把这4个小正方形全部破解,当然,如果有必要的话,少数其他的格子要先推出来。

又或者,在思考的时候,发现一些特别明显的格子,也可以写下来。

这样,数独就变成了下面这样:

这样,武士数独就直接变成了5个完全独立的数独。

上面2个是:

 

中间是:

下面2个是:

 

这样,武士数独就完全解决了,这个过程,花了我89分钟。

当然,我是用电脑自带的画图画的(全程口算无任何草稿),如果是用笔的话,

不仅写起来很快,而且可以在纸上打草稿,估计1个小时是可以解决的。

16,变种数独的规则退化现象

很多变种数独,在求解的过程中,会使得这附加的规则已经满足,

这个时候,规则退化,数独就变成了规则数独了。

例如:情人节趣味数独、箭头数独、对角线数独

但是,也有的变种数独,是基本上不会退化的。

只有当数独基本上已经解决了,只剩下极少数的格子的时候,才会退化,这相当于没有退化。

例如:窗口数独

而杀手数独,是真的永不退化。

puzzle(0112)不规则数独、变种数独相关推荐

  1. python37降到36原来的包还可以用吗_【lc刷题】36/37 有效的数独/解数独(143-144/300)...

    143-144/300 有效的数独 判断一个 9x9 的数独是否有效.只需要根据以下规则,验证已经填入的数字是否有效即可. 数字 1-9 在每一行只能出现一次. 数字 1-9 在每一列只能出现一次. ...

  2. 【数独】数独游戏-微信小程序开发流程详解

    有没有玩过数独游戏呢,听说,它是一个能训练大脑思维的棋盘类游戏,游戏规则很简单,通过小程序来实现很容易,非常适合对数独游戏逻辑感兴趣的同学,选择它开发入门吧. 准备 会使用微信开发者工具, 有Java ...

  3. “笨兔数独” 解数独软件 介绍及使用指南

    最近使用C语言+win32对话框写了一个解数独题目的小软件.在这里可以下载:笨兔数独V1. 现在对软件的功能及如何使用做一个简要的介绍. 程序界面 1>.程序各个按钮说明 start按钮 你可以 ...

  4. java数独中数独空格初始化,java高手近解决数独问题,看你是不是高手!

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 这个很简单的. #include #include #include using namespace std; int map[10][10]; char ...

  5. c 语言写数独游戏,经典数独游戏+数独求解器—纯C语言实现

    [转]NGUI研究院之三种方式监听NGUI的事件方法(七) NGUI事件的种类很多,比如点击.双击.拖动.滑动等等,他们处理事件的原理几乎万全一样,本文只用按钮来举例. 1.直接监听事件 把下面脚本直 ...

  6. php数独,php数独求解

    /* * @parm array &$data * @parm int $i  x * @parm int $j  y * return bool */ function checksd(&a ...

  7. [Python] 让AI来解决数独和数独谜题

  8. java完成九宫格数独_数独(简易九宫格) - 小黑202的个人空间 - OSCHINA - 中文开源技术交流社区...

    一.ViewController.h中代码 // //  ViewController.h //  ThirdSignleView // //  Created by on 15/6/10. //   ...

  9. python生成数独_数独生成与求解,Python实现

    2.[文件] Generate.py ~ 3KB 下载(44) #!/usr/bin/env python # -*- coding: utf-8 -*- # written by Kurt Wang ...

最新文章

  1. MySQL 用户与授权管理详解
  2. 【动态规划、计算几何】最优三角剖分
  3. mysqldump 导入数据库可能遇到错误
  4. golang Receiver has generic name 问题解决
  5. localhost访问IIS资源突然不好使了
  6. pandas 如何把时间转成index_pandas将字段中的字符类型转化为时间类型,并设置为索引...
  7. CentOS - 修改主机名教程(将 localhost.localdomain 改成其它名字)
  8. 21 RadioGroup ListFragment
  9. 【面向对象设计的5个原则】
  10. Git学习笔记总结和注意事项
  11. 面向对象-反射和__getattr__系列
  12. 【NeurIPS 2019】最大熵的蒙特卡洛规划算法
  13. 微软承认iPad牛逼,正计划为其定做Office?
  14. ut红种状态解决办法汇总
  15. mysql +cobar_转:阿里开源Mysql分布式中间件:Cobar
  16. python pkl、npy数据转换为csv
  17. 计算机如何更新目录,wps更新目录【设置办法】
  18. OpenCV手掌识别
  19. java中的数据解析是_Java从网络中请求获取JSon数据以及解析JSON数据----(自创,请注明)...
  20. strtok 详解及实现

热门文章

  1. codeforce比赛规则介绍(转)
  2. 如何1分钟实现身份实名认证功能?
  3. 微信小程序之获取百度语音合成
  4. 操作系统实验四 进程运行轨迹的跟踪与统计(哈工大李治军)
  5. 输入等值线参数绘制等值线图python_ArcGIS绘图—空气质量站点数据插值绘制等值线图...
  6. 视频号的发布技巧,视频号打造受欢迎的内容:国仁楠哥
  7. 为什么人脸识别系统总是认错黑人?
  8. 怎样使用JPQL查询出每个部门员工的平均工资和最高工资
  9. 联想开机启动项按哪个_联想重装系统按哪个键|联想电脑重装系统按什么键
  10. 学习OpenCV3:在空白图片上画虚线