我想大家小时候一定玩过八数码的游戏,如下图:在一个九宫格里面放入8个数字,数字只能上下左右移动,并且只能移动到空白处。通过若干此移动后,能把数字移动成图1.1右方所示图案。
下图是图1.1下一个格局的三种情况:
如果按正常的思维,采用盲目搜索的话,不仅搜索的次数多,而且往往容易陷入死循环中,所以面对此问题需要一种策略——启发式搜索
启发式搜索:启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提高了效率
解决此问题的启发策略:
每次移动的时候,正确位置数码的个数要大于交换前正确位置数码个数。
正确位置数码个数:每个数码的位置与最终格局的对比,如果位置相同,则说明此数码在正确位置。
如下图:
图1.3
图1.3中右边为最终格局,左边为当前格局,红色字体标识的数码为 正确位置数码,由此可以发现其正确位置的数码个数为4个。那么图1.2中正确数码如下图所示:
这样一步步的最终即可得到最终格局
源代码:
-
-
- #include"stdio.h"
- #define
num 3 //宏定义数码的行列数为3 -
-
- void
show(int begin[num][num]) - {
-
for(int i = 0; i < num; i++) -
{ -
for(int j = 0; j < num; j++) -
printf("%d ", begin[i][j]); -
printf("\n"); -
} -
printf("\n"); - }
-
-
- void
exchange(int begin[num][num], int row_one, int column_one, int row_two, int column_two) - {
-
int temp; -
temp = begin[row_two][column_two] ; -
begin[row_two][column_two] = begin[row_one][column_one]; -
begin[row_one][column_one] = temp; - }
-
-
- int
judge(int begin[num][num], int end[num][num]) - {
-
int count=0; //count记录数码中正确位置的个数 -
for(int i = 0; i < num; i++) //检查当前图形的正确度 -
for(int j = 0; j < num; j++) -
{ -
if(begin[i][j] == end[i][j] && end[i][j] != 0) -
count++; -
} -
return count; //返回数码中正确位置的个数 - }
-
-
- int
yidong(int begin[num][num], int end[num][num] -
, int right, int jishu, int ji_shu[50][3][3] -
, int biaoji, int row, int column) //biaoji存储上一轮移动的反方向代号 - {
-
int temp_zhi; -
show(begin); //显示数组矩阵 -
if(jishu >= 20) -
return 0; -
int node; //,node为标记 -
int temp; //存储当前待调整数码正确的个数 -
for(int q=0; q//检查交换后的end[][]图形是否先前已经遍历过了 -
{ -
node = 1; -
for(int w=0; w -
for(int r=0; r -
if(ji_shu[q][w][r] != begin[w][r]) -
node = 0; -
if(node == 1) //如果先前遍历过,返回0 -
{ -
return 0; -
} -
} -
for(int i = 0; i < num; i++) -
for(int j = 0; j < num; j++) -
ji_shu[jishu][i][j] = begin[i][j]; -
-
if(right == num * num - 1) //如果待调整数码与最终数码完全相同时,返回1 -
return 1; -
if(row > 0 && biaoji != 0) //存储0的位置不是在第一行 -
{ -
exchange(begin, row - 1, column, row , column); //将0与其上面的元素交换存储位置 -
temp = judge(begin, end); -
if(temp < right) //如果交换后正确数码的个数不大于原来正确数码的个数 -
exchange(begin, row - 1, column, row , column); //再将其交换回来 -
else if(temp >= right) //如果交换后正确数码的个数大于或等于原来正确数码的个数 -
{ -
temp_zhi = yidong(begin, end, temp, jishu+1, ji_shu, 2, row-1, column); -
if( temp_zhi == 1) //进行下一步的移动 -
return 1; -
exchange(begin, row - 1, column, row , column); //再将其交换回来 -
} -
} -
if(column > 0 && biaoji != 1) -
{ -
exchange(begin, row, column - 1, row , column); //将0与其左边的元素交换存储位置 -
temp = judge(begin, end); -
if(temp < right) -
exchange(begin, row, column - 1, row , column); -
else if(temp >= right) -
{ -
temp_zhi = yidong(begin, end, temp, jishu+1, ji_shu ,3, row, column - 1); -
if(temp_zhi == 1) -
return 1; -
exchange(begin, row, column - 1, row , column); -
} -
} -
-
if(row < num-1 && biaoji != 2) -
{ -
exchange(begin, row + 1, column, row , column); //将0与其下面的元素交换存储位置 -
temp = judge(begin, end); -
if(temp < right) -
exchange(begin, row + 1, column, row , column); -
else if(temp >= right) -
{ -
temp_zhi =yidong(begin, end, temp, jishu+1, ji_shu, 0, row+1, column); -
if(temp_zhi == 1) -
return 1; -
exchange(begin, row + 1, column, row , column); -
} -
} -
if(column < num-1 && biaoji != 3) -
{ -
exchange(begin, row, column + 1, row , column); //将0与其右边的元素交换存储位置 -
temp = judge(begin, end); -
if(temp < right) -
exchange(begin, row, column + 1, row , column); -
else if(temp >= right) -
{ -
temp_zhi = yidong(begin, end, temp, jishu+1, ji_shu, 1, row, column+1); -
if(temp_zhi == 1) -
return 1; -
exchange(begin, row, column + 1, row , column); -
} -
} -
return 0; //移动失败,返回0 - }
-
-
- void
shuru(int begin[][num],int blank[]) - {
-
int temp, node, zero = 0; -
for (int i = 0; i < num; i++) -
for(int j = 0; j < num; j++) -
{ -
node = 1; -
printf("请输入第%d行,第%d列的元素的值:", i+1, j+1); -
scanf("%d", &temp); -
for (int q = 0; q <= i && node == 1; q++) //当输入的值有重复的,提示重新输入 -
for (int w = 0; w < j; w++) -
if(temp == begin[q][w]) -
{ -
printf("输入重复,请重新输入\n"); -
node = 0; -
j--; -
break; -
} -
if(temp < 0 || temp > num*num-1) //当输入的值不是在数码的区间范围内时,提示重新输入 -
{ -
printf("请输入从%d到%d的数\n", zero, num*num-1); -
node = 0; -
j--; -
} -
if(node == 1) //如果输入满足条件 -
{ -
if(temp == 0) //如果输入的值为零,由blank[0]记录行号,blank[1]记录列号 -
{ -
blank[0] = i; -
blank[1] = j; -
} -
begin[i][j] = temp;//将满足条件的值存储起来 -
} -
} - }
-
- int
main() - {
-
int jishu = 0, ji_shu[50][3][3];//jishu存储已经遍历过的八数码图形的个数,jishu[][][]存储已经遍历过的八数码图形的形状 -
int row; //存储数字零的行数 -
int column; //存储数字零的列数 -
int begin[num][num], blank[2],count=1; -
int end[num][num] = {1, 2, 3, 8, 0, 4, 7, 6, 5}; //给最终状态的数码矩阵赋值 -
printf ("-------%d数码游戏开始!--------\n", num); -
shuru(begin, blank); //输入带调整状态的数码矩阵的值 -
row = blank[0]; -
column = blank[1]; -
if(yidong (begin, end,judge(begin,end),jishu,ji_shu,4,row,column) == 0) -
printf("\n此8数码的问题可能无解!"); -
else -
show(begin); -
getchar();getchar(); -
return 0; - }
总结:此算法运用了启发式搜索策略,根据正确位置数码个数选择下一步移动方案。虽然此算法能够解决八数码问题,但是移动的次数不是最少,因而再次算法上升级
转自:http://blog.youkuaiyun.com/q345852047/article/details/6626684