1。“数独”游戏简介:
数独(日语:数独 すうどく)是一种源自18世纪末的瑞士,后在美国发展、并在日本得以发扬光大的数学智力拼图游戏。拼图是九宫格(即3格宽×3格高)的正方形状,每一格又细分为一个九宫格。在每一个小九宫格中,分别填上1至9的数字,让整个大九宫格每一列、每一行的数字都不重复。
数独的玩法逻辑简单,数字排列方式千变万化。不少教育者认为数独是锻炼脑筋的好方法。
如图:
2。算法思路:
(1)对于每一个数字分析所有点,如果同行,同列,同宫(宫代表3*3小格)有这个数字,则标记此点不能填此数字。如果同宫内只有一个点可以填这个数字,则填上。
(2)如果一点的同行,同列,同宫内已经有了1-9这9个数字中的8个,则填上最后一个。
(3)反复循环(1)(2)两步,直到通过上两个条件已无法新填入数字。
(4)如果此时所有点都填完,找到此题的唯一解,输出,程序结束;如果此时图中仍有空点,说明此题可能有多个解。找到填入可能性较少的点,猜测一个值。之后回到步骤(1),循环。
注:<1> 当可能有多个解时,此程序只输出最先找到的一个解,而不是所有解。
<2> 如果题目已知的数字过少(如只已知一个数字,或一个都不知道,只是一张空白表),则会造成搜索空间过大。通过网上看题,可把阈值设为一张图最少要填入17个数字。
3。code
bool game
::
analyze(
int
a[][Dim])
{
bool flag1 = true , flag2 = true;
// 遍历整表,如果通过不重分析填入过数字,则flag1置真
// 遍历整表,如果通过不漏分析填入过数字,则flag2置真
int k , i , j; // i , j为循环变量 , k表示此格的值
bool tag[Dim][Dim]; // 对于一个数( 1 - 9 ) , 判断(i , j)点上是否可以写入这个数
bool tagRow[Dim] , tagLine[Dim] , tagSmallTable[Dim] , tagTotal[Dim];
// tagRow纪录一行中是否包含1 - 9 , tagLine纪录一列中是否包含1 - 9 ,
// tagSmall纪录3 * 3小格中是否包含1 - 9 , tagTotal纪录一点同行、列、 3 * 3小格三方面是否包含1 - 9
int existCount;
// 纪录一点同行、列、 3 * 3小格三方面共包含1 - 9中9个数字其中的几个(用于回朔)
while (flag1 || flag2)
{
while (flag1) // 不重分析
{
flag1 = false;
for (k = 1 ; k <= 9 ; k ++ )
{
for (i = 0 ; i < Dim; i ++ ) // tag数组全置false
for (j = 0 ; j < Dim; j ++ )
{
tag[i][j] = false;
}
for (i = 0 ; i < Dim; i ++ ) // 察看对所有点来说哪些点可以填入数字k
for (j = 0 ; j < Dim; j ++ )
{
// 如果(此点无数 && 同行没有k && 同列没有k && 所在3 * 3格没有k) 则此点可写入k
if (a[i][j] == 0 && rowExist(a , i , k) == false && lineExist(a , j , k) == false
&& smallTableExist(a , i , j , k) == false)
tag[i][j] = true;
}
for (i = 0 ; i < Dim; i ++ ) // 对于一个3 * 3格内如果只有一个点可以填入数字k , 则填入
for (j = 0 ; j < Dim; j ++ )
{
if (tag[i][j] == true
&& smallTableDoubleExist(tag , i , j) == false)
{
a[i][j] = k;
flag1 = true; // 如果此次循环有内容写入,则flag1置真
break;
}
}
} // end for (k)
} // end while (flag1)
while (flag2) // 不漏分析,查看此点所在的行、列、小格中是否包含1 - 9
{
flag2 = false;
for (i = 0 ; i < Dim; i ++ )
for (j = 0 ; j < Dim; j ++ )
{
for (k = 0 ; k < 9 ; k ++ )
tagRow[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagLine[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagSmallTable[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagTotal[k] = false;
existCount = 0 ;
if (a[i][j] == 0 ) // 如果此点无值
{
checkRow(a , i , j , tagRow);
checkLine(a , i , j , tagLine);
checkSmallTable(a , i , j , tagSmallTable);
checkTotal(tagRow , tagLine , tagSmallTable , tagTotal);
existCount = existCounting(tagTotal);
if (existCount == 8 ) // 如果此点无值并且其同行、列、 3 * 3小格内已有1 - 9中9个数字的8个
{
a[i][j] = filllast(tagTotal); // 那么此点填入最后一个数字
flag2 = true;
break;
}
}
}
} // end while (flag2)
} // end while (flag1 || flag2)
/* 查看经过上面一轮填入后表内是否还存在空点,如果还存在空点,说明下面内容需要猜测
同时也说明此题可能有多个解(此程序之给出多个解中的一个解,而不是所有解) */
bool zeroExist;
zeroExist = false;
for (i = 0 ; i < Dim; i ++ )
{
for (j = 0 ; j < Dim; j ++ )
{
if (a[i][j] == 0 )
{
zeroExist = true;
break;
}
}
if (zeroExist)
{
break;
}
}
if (zeroExist)
{
int maxExistCount = 1 ;
int tempi = 0 , tempj = 0 ;
for (i = 0 ; i < Dim; i ++ )
for (j = 0 ; j < Dim; j ++ )
{
for (k = 0 ; k < 9 ; k ++ )
tagRow[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagLine[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagSmallTable[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagTotal[k] = false;
existCount = 0 ;
if (a[i][j] == 0 )
{
checkRow(a , i , j , tagRow);
checkLine(a , i , j , tagLine);
checkSmallTable(a , i , j , tagSmallTable);
checkTotal(tagRow , tagLine , tagSmallTable , tagTotal);
existCount = existCounting(tagTotal);
if (existCount > maxExistCount) // 找出猜测时有较少可能性的点作为突破口
{
maxExistCount = existCount;
tempi = i;
tempj = j;
}
}
}
int b[Dim][Dim];
int c[Dim][Dim];
for (k = 0 ; k < 9 ; k ++ )
tagRow[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagLine[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagSmallTable[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagTotal[k] = false;
existCount = 0 ;
for (i = 0 ; i < Dim; i ++ )
for (j = 0 ; j < Dim; j ++ )
b[i][j] = a[i][j];
checkRow(a , tempi , tempj , tagRow);
checkLine(a , tempi , tempj , tagLine);
checkSmallTable(a , tempi , tempj , tagSmallTable);
checkTotal(tagRow , tagLine , tagSmallTable , tagTotal);
existCount = existCounting(tagTotal);
if (existCount == 9 )
{
return false; // 说明上一个猜测处猜错,回朔
}
else
{
for (i = 0 ; i < Dim; i ++ ) // 对于需要猜测点的各种可能性全部循环
{
if ( ! tagTotal[i])
{
int m , n; // 纪录下此时内容,在猜错后回朔时用到
for (m = 0 ; m < Dim; m ++ )
for (n = 0 ; n < Dim; n ++ )
c[m][n] = 0 ;
for (m = 0 ; m < Dim; m ++ )
for (n = 0 ; n < Dim; n ++ )
c[m][n] = b[m][n];
b[tempi][tempj] = i + 1 ;
if ( ! analyze(b)) // 递归,如果analyze(b)为假,说明猜错,恢复现场
{
for (m = 0 ; m < Dim; m ++ )
for (n = 0 ; n < Dim; n ++ )
b[m][n] = c[m][n];
}
}
}
return false; // 如果i超出了Dim也说明前面猜测猜错了
}
}
else // 表中不存在0了,说明全部填完了
{
std :: cout << std :: endl << " 结果为: " << std :: endl;
showtable(a);
std :: cout << std :: endl << " 输入任意字符后按enter键结束 " ;
char screenStop; // 让程序屏幕停住
std :: cin >> screenStop;
exit ( 0 ); // 程序出口
}
}
{
bool flag1 = true , flag2 = true;
// 遍历整表,如果通过不重分析填入过数字,则flag1置真
// 遍历整表,如果通过不漏分析填入过数字,则flag2置真
int k , i , j; // i , j为循环变量 , k表示此格的值
bool tag[Dim][Dim]; // 对于一个数( 1 - 9 ) , 判断(i , j)点上是否可以写入这个数
bool tagRow[Dim] , tagLine[Dim] , tagSmallTable[Dim] , tagTotal[Dim];
// tagRow纪录一行中是否包含1 - 9 , tagLine纪录一列中是否包含1 - 9 ,
// tagSmall纪录3 * 3小格中是否包含1 - 9 , tagTotal纪录一点同行、列、 3 * 3小格三方面是否包含1 - 9
int existCount;
// 纪录一点同行、列、 3 * 3小格三方面共包含1 - 9中9个数字其中的几个(用于回朔)
while (flag1 || flag2)
{
while (flag1) // 不重分析
{
flag1 = false;
for (k = 1 ; k <= 9 ; k ++ )
{
for (i = 0 ; i < Dim; i ++ ) // tag数组全置false
for (j = 0 ; j < Dim; j ++ )
{
tag[i][j] = false;
}
for (i = 0 ; i < Dim; i ++ ) // 察看对所有点来说哪些点可以填入数字k
for (j = 0 ; j < Dim; j ++ )
{
// 如果(此点无数 && 同行没有k && 同列没有k && 所在3 * 3格没有k) 则此点可写入k
if (a[i][j] == 0 && rowExist(a , i , k) == false && lineExist(a , j , k) == false
&& smallTableExist(a , i , j , k) == false)
tag[i][j] = true;
}
for (i = 0 ; i < Dim; i ++ ) // 对于一个3 * 3格内如果只有一个点可以填入数字k , 则填入
for (j = 0 ; j < Dim; j ++ )
{
if (tag[i][j] == true
&& smallTableDoubleExist(tag , i , j) == false)
{
a[i][j] = k;
flag1 = true; // 如果此次循环有内容写入,则flag1置真
break;
}
}
} // end for (k)
} // end while (flag1)
while (flag2) // 不漏分析,查看此点所在的行、列、小格中是否包含1 - 9
{
flag2 = false;
for (i = 0 ; i < Dim; i ++ )
for (j = 0 ; j < Dim; j ++ )
{
for (k = 0 ; k < 9 ; k ++ )
tagRow[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagLine[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagSmallTable[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagTotal[k] = false;
existCount = 0 ;
if (a[i][j] == 0 ) // 如果此点无值
{
checkRow(a , i , j , tagRow);
checkLine(a , i , j , tagLine);
checkSmallTable(a , i , j , tagSmallTable);
checkTotal(tagRow , tagLine , tagSmallTable , tagTotal);
existCount = existCounting(tagTotal);
if (existCount == 8 ) // 如果此点无值并且其同行、列、 3 * 3小格内已有1 - 9中9个数字的8个
{
a[i][j] = filllast(tagTotal); // 那么此点填入最后一个数字
flag2 = true;
break;
}
}
}
} // end while (flag2)
} // end while (flag1 || flag2)
/* 查看经过上面一轮填入后表内是否还存在空点,如果还存在空点,说明下面内容需要猜测
同时也说明此题可能有多个解(此程序之给出多个解中的一个解,而不是所有解) */
bool zeroExist;
zeroExist = false;
for (i = 0 ; i < Dim; i ++ )
{
for (j = 0 ; j < Dim; j ++ )
{
if (a[i][j] == 0 )
{
zeroExist = true;
break;
}
}
if (zeroExist)
{
break;
}
}
if (zeroExist)
{
int maxExistCount = 1 ;
int tempi = 0 , tempj = 0 ;
for (i = 0 ; i < Dim; i ++ )
for (j = 0 ; j < Dim; j ++ )
{
for (k = 0 ; k < 9 ; k ++ )
tagRow[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagLine[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagSmallTable[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagTotal[k] = false;
existCount = 0 ;
if (a[i][j] == 0 )
{
checkRow(a , i , j , tagRow);
checkLine(a , i , j , tagLine);
checkSmallTable(a , i , j , tagSmallTable);
checkTotal(tagRow , tagLine , tagSmallTable , tagTotal);
existCount = existCounting(tagTotal);
if (existCount > maxExistCount) // 找出猜测时有较少可能性的点作为突破口
{
maxExistCount = existCount;
tempi = i;
tempj = j;
}
}
}
int b[Dim][Dim];
int c[Dim][Dim];
for (k = 0 ; k < 9 ; k ++ )
tagRow[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagLine[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagSmallTable[k] = false;
for (k = 0 ; k < 9 ; k ++ )
tagTotal[k] = false;
existCount = 0 ;
for (i = 0 ; i < Dim; i ++ )
for (j = 0 ; j < Dim; j ++ )
b[i][j] = a[i][j];
checkRow(a , tempi , tempj , tagRow);
checkLine(a , tempi , tempj , tagLine);
checkSmallTable(a , tempi , tempj , tagSmallTable);
checkTotal(tagRow , tagLine , tagSmallTable , tagTotal);
existCount = existCounting(tagTotal);
if (existCount == 9 )
{
return false; // 说明上一个猜测处猜错,回朔
}
else
{
for (i = 0 ; i < Dim; i ++ ) // 对于需要猜测点的各种可能性全部循环
{
if ( ! tagTotal[i])
{
int m , n; // 纪录下此时内容,在猜错后回朔时用到
for (m = 0 ; m < Dim; m ++ )
for (n = 0 ; n < Dim; n ++ )
c[m][n] = 0 ;
for (m = 0 ; m < Dim; m ++ )
for (n = 0 ; n < Dim; n ++ )
c[m][n] = b[m][n];
b[tempi][tempj] = i + 1 ;
if ( ! analyze(b)) // 递归,如果analyze(b)为假,说明猜错,恢复现场
{
for (m = 0 ; m < Dim; m ++ )
for (n = 0 ; n < Dim; n ++ )
b[m][n] = c[m][n];
}
}
}
return false; // 如果i超出了Dim也说明前面猜测猜错了
}
}
else // 表中不存在0了,说明全部填完了
{
std :: cout << std :: endl << " 结果为: " << std :: endl;
showtable(a);
std :: cout << std :: endl << " 输入任意字符后按enter键结束 " ;
char screenStop; // 让程序屏幕停住
std :: cin >> screenStop;
exit ( 0 ); // 程序出口
}
}