马的遍历问题(自己的一些见解,可能是拙略的)

本文讨论了马在M*N网格上按日字形移动,使所有格子变黑的问题。提出了打表遍历和随机搜索两种方法。打表遍历通过建立二维数组记录路径,但效率较低;随机数搜索法通过不断生成随机走法,直至找到解决方案,时间消耗与网格大小相关。

问题原型:

一只马在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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值