问题原型:
一只马在M*N的网格上跳来跳去,马走日,它走过的格子会变为黑色,请问它应如何才能将所有格子全部黑色?这个问题来源自微信上的一个游戏,当时是6*6的格子,我想很多方法都没有成功,最终总是有一个格子不能变成黑色。这里给出两种基本的方法:打表遍历和随机遍历(虽然有回朔法,但是本人能力有限,实现后会公布在这里。)
打表遍历:
不难想象,处于中间位置的马有8种走法(如果马的原始坐标为(0,0),则有(1,2)(2,1)(-1,2)(-2,1)(1,-2)(2,-1)(-1,-2)(-2,-1)共8种走法)。给这八种走法编号0-7,一共M*N步(假设走完),编程实现时可以先建立一个M*N的二维数组,并且建立一个长度为M*N的一维数组作为每一次遍历的路径指示。不断更新一维数组,直到找到目标路径。这里是实现的源代码:
#include<iostream>
#include<cstdlib>
using namespace std;
#define FAILED false
#define SUCCEED true
#define Len 5 //长度5
#define Wid 5 //宽度5
const int size = Len*Wid;
int currentx, currenty; //记录当前马所处的位置
long long times = 0; //记录遍历的次数
bool Go(int Array[Len][Wid],int Table[]); //走子函数
bool Vaild(int move,int Array[Len][Wid]); //结合当前马的位置判断Table[]中的移位是否合法
bool TableGenerate(int Table[]); //更新表Table[]
void OutPut(int Table[]); //输出表Table[]
int main()
{
int Array[Len][Wid];
memset(Array, 0, size*sizeof(int));
int Table[size] = { 0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0 };//表的初始状态(后面会说明)
cout << "请输入起始坐标:" << endl;
cin >> currentx >> currenty;
for (;;)
{
if (times % 1000000 == 0) //避免输出变得太快
{
cout << times << " tried. Route:";
OutPut(Table);
cout << endl;
}
if (Go(Array, Table) == SUCCEED) //如果走子函数返回成功,则输出所找到的路径
{
cout << "以找到路径:";
OutPut(Table);
system("pause");
}
else
{
memset(Array, 0, sizeof(int)* 4); //如果没有找到,则重置表格
}
if (TableGenerate(Table) == FAILED) //如果表已经遍历完,结束循环
{
cout << "已完成所有穷举!" << endl;
break;
}
times++;
}
system("pause");
return 0;
}
bool Go(int Array[Len][Wid], int Table[])
{
bool ret;
for (int i = 0; i < Len*Wid; i++)
{
ret = Vaild(Table[i], Array);
if (ret)
{
Array[currentx][currenty] = 1; //已经走过的位置标记为1
}
else
{
return FAILED;
}
}
return ret;
}
bool Vaild(int move,int Array[Len][Wid])
{
int x = currentx, y = currenty;
switch (move)
{
case 0:
x++;
y -= 2;
break;
case 1:
x += 2;
y--;
break;
case 2:
x += 2;
y++;
break;
case 3:
x++;
y += 2;
break;
case 4:
x--;
y += 2;
break;
case 5:
x -= 2;
y++;
break;
case 6:
x -= 2;
y--;
break;
case 7:
x--;
y -= 2;
break;
}
if (x >= 0 && x < Len && y >= 0 && y < Wid && Array[x][y]!=1) //如果所走的目标位置在范围内并且没有走过,则更新马的位置
{
currentx = x, currenty = y;
return SUCCEED;
}
return FAILED;
}
bool TableGenerate(int Table[])
{
Table[0]++;
for (int i = 0; i<size - 1; i++)//最高位单独处理,如果表的最高位变成8则停止搜索
{
if (Table[i] == Table[i + 1] && Table[i] == Table[i + 2]) //后面解释
{
Table[i]++;
}
if (Table[i]>7) //进位处理
{
Table[i + 1] ++;
Table[i] -= 8;
}
}
if (Table[size-1] > 7)//最高位变成8,完成搜索
{
return FAILED;
}
return SUCCEED;
}
void OutPut(int Table[])
{
for (int i = 0; i < size; i++)
{
cout << Table[i];
}
}现在解释Table[]初始化的原因以及表的生成函数设计思想。由于当前是5*5表格,所以如果Table中出现三个数字相同,即连续向同样一个个位置走3次,则肯定是无效表,而如果将表初始化为全0,在很长一段时间内都一定不会有结果,因为最后几步都是0。而代码中给的就是搜索的起始点(之前的都不是有效路径)。而表的生成函数设计也是遵循这个原则,如表中有这样的序列:1102230222........出现了3个2,则让第一个2变成3,之后继续打表即可。
但是出现的问题是,这样打标速度很慢。我电脑跑了一上午还没出一个结果。
随机数搜索法:
不停地让计算机产生随机数,范围(1-8)并且结合当前位置判断随机数所指定的落子方向是否合法,若不合法重新产生,合法则落子,不断重复这个过程直到找到结果为止。但这种方法搜索时间不定(通常情况下5*5的表格十几秒就可以得到结果,6*6的1分钟之内,7*7的还没有尝试,这里给出5*5的实现,其余的类似,代码之前写的,比较乱)
#include<iostream>
#include<Windows.h>
#include<math.h>
#include<time.h>
using namespace std;
class PlantArray
{
private:
int parray[5][5];
int px, py; //记录当前的位置
public:
int times;
int moves;//移动方向
PlantArray(int x,int y);//初始化函数
bool JudgeStat() const;//状态判断
void EvaluateAndMove();//评估并移动
bool GameOverEvaluate();//判断是否失败
void Clear(int x,int y);//清空
void Output();
};
int main()
{
int x = 2, y = 2;
srand(time(NULL));
PlantArray Gird(x,y);//创建表格,马的初始位置为(2,2)
for (;;)
{
Gird.moves = rand() % 8 + 1;
Gird.EvaluateAndMove();
if (Gird.GameOverEvaluate() == 0)
{
if (Gird.JudgeStat() == 0)
{
break;
}
Gird.Clear(x,y);
//system("pause");
}
}
system("pause");
return 0;
}
PlantArray::PlantArray(int x, int y)
{
memset(parray, 0, 25 * sizeof(int));
px = x, py = y;
parray[x][y] = 1;
times = 0;
}
bool PlantArray::JudgeStat() const
{
short int flag = 0;
for (int t1 = 0; t1 < 5; t1++)
{
for (int t2 = 0; t2 < 5; t2++)
{
if (parray[t1][t2] == 0)
{
flag=1;
goto End;
}
}
}
return 0;
End:
return 1;
}
void PlantArray::EvaluateAndMove()
{
int x = px, y = py;
switch (moves)
{
case 1:
x++;
y -= 2;
if (x>=0 && x<=4 && y>=0 && y<=4 && parray[x][y]!=1)
{
px = x;
py = y;
parray[px][py] = 1;
Output();
}
break;
case 2:
x += 2;
y--;
if (x>=0 && x<=4 && y>=0 && y<=4 && parray[x][y]!=1)
{
px = x;
py = y;
parray[px][py] = 1;
Output();
}
break;
case 3:
x += 2;
y++;
if (x>=0 && x<=4 && y>=0 && y<=4 && parray[x][y]!=1)
{
px = x;
py = y;
parray[px][py] = 1;
Output();
}
break;
case 4:
x++;
y += 2;
if (x>=0 && x<=4 && y>=0 && y<=4 && parray[x][y]!=1)
{
px = x;
py = y;
parray[px][py] = 1;
Output();
}
break;
case 5:
x--;
y += 2;
if (x>=0 && x<=4 && y>=0 && y<=4 && parray[x][y]!=1)
{
px = x;
py = y;
parray[px][py] = 1;
Output();
}
break;
case 6:
x -= 2;
y++;
if (x>=0 && x<=4 && y>=0 && y<=4 && parray[x][y]!=1)
{
px = x;
py = y;
parray[px][py] = 1;
Output();
}
break;
case 7:
x -= 2;
y--;
if (x>=0 && x<=4 && y>=0 && y<=4 && parray[x][y]!=1)
{
px = x;
py = y;
parray[px][py] = 1;
Output();
}
break;
case 8:
x--;
y -= 2;
if (x>=0 && x<=4 && y>=0 && y<=4 && parray[x][y]!=1)
{
px = x;
py = y;
parray[px][py] = 1;
Output();
}
}
}
bool PlantArray::GameOverEvaluate()
{
if (px + 1 >= 0 && px + 1 <= 4 && py - 2 >= 0 && py - 2 <= 4 && parray[px + 1][py - 2] != 1)
{
return 1;
}
if (px + 2 >= 0 && px + 2 <= 4 && py - 1 >= 0 && py - 1 <= 4 && parray[px + 2][py - 1] != 1)
{
return 1;
}
if (px + 2 >= 0 && px + 2 <= 4 && py + 1 >= 0 && py + 1 <= 4 && parray[px + 2][py + 1] != 1)
{
return 1;
}
if (px + 1 >= 0 && px + 1 <= 4 && py + 2 >= 0 && py + 2 <= 4 && parray[px + 1][py + 2] != 1)
{
return 1;
}
if (px - 1 >= 0 && px - 1 <= 4 && py + 2 >= 0 && py + 2 <= 4 && parray[px - 1][py + 2] != 1)
{
return 1;
}
if (px - 2 >= 0 && px - 2 <= 4 && py + 1 >= 0 && py + 1 <= 4 && parray[px - 2][py + 1] != 1)
{
return 1;
}
if (px - 2 >= 0 && px - 2 <= 4 && py - 1 >= 0 && py - 1 <= 4 && parray[px - 2][py - 1] != 1)
{
return 1;
}
if (px - 1 >= 0 && px - 1 <= 4 && py - 2 >= 0 && py - 2 <= 4 && parray[px - 1][py - 2] != 1)
{
return 1;
}
times++;
return 0;
}
void PlantArray::Clear(int x,int y)
{
memset(parray, 0, 25 * sizeof(int));
px = x, py = y;
parray[x][y] = 1;
}
void PlantArray::Output()
{
/*cout << endl;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
cout << parray[i][j]<<" ";
}
cout << endl;
}*/
cout << "Current Position:(" << px << "," << py << ") "<<"Times tried: "<<times;
}
本文讨论了马在M*N网格上按日字形移动,使所有格子变黑的问题。提出了打表遍历和随机搜索两种方法。打表遍历通过建立二维数组记录路径,但效率较低;随机数搜索法通过不断生成随机走法,直至找到解决方案,时间消耗与网格大小相关。
&spm=1001.2101.3001.5002&articleId=54767413&d=1&t=3&u=67973ce45ba3486fb9265b61ef8493a1)
1万+

被折叠的 条评论
为什么被折叠?



