马踏棋盘,贪心算法

由于回溯法实在是太耗费时间,所以利用贪心算法进行了优化,在阅读了网上关于贪心算法怎么用之后又调试了很久,终于在上一个回溯法的基础上写出了优化的算法,但是由于本人水平实在有限,代码或多或少还是有些问题,优化的可能还不够。希望自己能继续进步吧。
所谓贪心算法:贪心算法(又称贪婪算法),是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。它只考虑局部的最优,从而让总体也最优。对于马踏棋盘来说,我们每一步的选择总是选择下一步的下一步可选择数最少的那一步,简单点说,就是比如我下一次有的可选择方案有a,b,c三种,然后a,b,c三种方案的下一步分别又有5,3,2种选择,那么我们下一步就选择c方案。
那么我们只需要在原来的算法上在选择的时候进行一个判断就行了,原来我们的算法是,只有找到了一种方案就执行,不管他的下一步,就会造成下一步的下一步无路可走,然后又回溯到上一步重新选择。我在调试的过程中发现我们选择的是下一步的下一步方案最少的方案,那么就能保证下一步的下一步不会碰壁,就这样一直递归下去不会产生回溯,但是到了倒数第二步,也即是第63步,他的下一步只有一种选择,他的下一步的下一步没有选择了,结束了,所以就需要在中间加上判断,如果到了倒数第二步,就应该找到可行方案直接走。
代码如下:

#include <stdio.h>
#include <time.h> //包含该头文件,以便计算程序用时

//棋盘大小
#define X 8
#define Y 8
//用二维数组表示棋盘
int chess[X][Y];


//打印棋盘,棋盘是全局变量,这里就不写函数参数了
void print()
{
	for (int i = 0; i < Y; i++)
	{
		for (int j = 0; j < X; j++)
		{
			//输出长度为2,不足左补空格
			printf("%2d  ", chess[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
//x,y表示当前位置,count表示相对于位置(x,y)的8种走法中该走哪一种
int nextxy(int &x, int &y, int count)
{
	//8种选择是x加两格,x退两格与y退一格,y加一格组合,有四种
	//x退一格,x加一格与y加两格,y退两格组合,有四种,一共8种
	switch (count)
	{
	case 0:
		//首先位置要有效,其次该位置没有走过
		if (x + 2 <= X - 1 && y + 1 <= Y - 1 && chess[x + 2][y + 1] == 0)
		{
			//如果有效位置,则将(x,y)改为马下一次要到的位置
			x += 2;
			y += 1;
			return 1;
		}
		break;
	case 1:
		//首先位置要有效,其次该位置没有走过
		if (x + 2 <= X - 1 && y -1 >= 0 && chess[x + 2][y - 1] == 0)
		{
			//如果有效位置,则将(x,y)改为马下一次要到的位置
			x += 2;
			y -= 1;
			return 1;
		}
		break;
	case 2:
		//首先位置要有效,其次该位置没有走过
		if (x - 2 >= 0 && y - 1 >= 0 && chess[x - 2][y - 1] == 0)
		{
			//如果有效位置,则将(x,y)改为马下一次要到的位置
			x -= 2;
			y -= 1;
			return 1;
		}
		break;
	case 3:
		//首先位置要有效,其次该位置没有走过
		if (x - 2 >= 0 && y + 1 <= Y-1 && chess[x - 2][y + 1] == 0)
		{
			//如果有效位置,则将(x,y)改为马下一次要到的位置
			x -= 2;
			y += 1;
			return 1;
		}
		break;
	case 4:
		if (x + 1 <= X - 1 && y + 2 <= Y - 1 && chess[x + 1][y + 2] == 0)
		{
			x += 1;
			y += 2;
			return 1;
		}
		break;
	case 5:
		if (x + 1 <= X - 1 && y - 2 >= 0 && chess[x + 1][y - 2] == 0)
		{
			x += 1;
			y -= 2;
			return 1;
		}
		break;
	case 6:
		if (x - 1 >= 0 && y + 2 <= Y - 1 && chess[x - 1][y + 2] == 0)
		{
			x -= 1;
			y += 2;
			return 1;
		}
		break;
	case 7:
		if (x - 1 >= 0 && y - 2 >= 0 && chess[x - 1][y - 2] == 0)
		{
			x -= 1;
			y -= 2;
			return 1;
		}
		break;
	default:
		break;
	}
	//返回0,需要重新寻找
	return 0;
}
//寻找该节点的所有的有效位置,返回有效位置的个数
int numofpos(int x, int y)
{
	int count = 0;
	int num = 0;
	int x1 = x, y1 = y;
	int flag = nextxy(x1, y1, count);
	while (count <= 7)
	{
		if (flag==1)
			num++;
		count++;
		x1 = x, y1 = y;
		flag = nextxy(x1, y1, count);
	}
	return num;
}

//选择下一个节点的下一选择数最少的那个
//nextnum存放的是下一个节点的下一个选择的个数,
//num是数组大小
int Minchoose(int nextnum[], int num)
{
	int min = 0;
	int index = 0;
	//找到第一个下一个选择不为零的位置
	for (int i = 0; i < num; i++)
	{
		if (nextnum[i]>0)
		{
			min = nextnum[i];
			index = i;
			break;
		}
	}
	for (int i = 1; i < num; i++)
	{
		//找到选择数大于0且最小的下一个节点
		if (nextnum[i]>0 && nextnum[i] < min)
		{
			min = nextnum[i];
			index = i;
		}
			
	}
	
	if (nextnum[index]>0)
		return index;
	//如果不存在选择数大于0且数目最小的位置,返回-1,通知它的下一步没有路了,也就是到了最后一步。
	return -1;

}
//遍历算法
//x,y表示位置,tag表示当前走了几格,第一次开始时该值应为1
int TravelChess(int x, int y, int tag)
{
	chess[x][y] = tag;
	//如果走了X*Y格,说明全部遍历完,打印棋盘
	if (tag == X*Y)
	{
		print();
		return 1;//返回1表示遍历完成
	}
	//如果没有遍历完
	int x1 = x, y1 = y;
	
	//寻找下一个有效位置
	int count = 0;
	int flag = nextxy(x1, y1, count);
	//存放(x,y)所有有效位置的下一个有效位置的个数,有效位置的编号是0~7
	int num[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

	//每找到一个有效位置,就计算该有效位置的下一位有多少种选择
	while (count <= 7)
	{
		if (flag == 1)
		{
			//计算对于有效位置count的下一位有多少种选择
			num[count] = numofpos(x1, y1);
		}

		//继续
		count++;
		x1 = x, y1 = y;
		flag = nextxy(x1, y1, count);
	}


	int index = Minchoose(num, 8);
	x1 = x, y1 = y;
	//说明到了最后一步,找到可行方案即可
	if (index == -1)
	{
		count = 0;
		flag = nextxy(x1, y1, count);
		while (flag == 0 && count <= 7)
		{
			count++;
			x1 = x;
			y1 = y;
			flag = nextxy(x1, y1, count);
		}
	}
	else
	{
		//将下一个位置选择第index个
		flag = nextxy(x1, y1, index);
	}
	
	//如果找到了下一个有效位置,该有效位置作为(x,y),继续调用自身进行相同的搜索
	if (TravelChess(x1, y1, tag + 1))
	{
		return 1;
	}
	return 0;
}
int main()
{
	//记录开始时间
	clock_t start = clock();
	//以防万一,初始化棋盘都为0
	for (int i = 0; i < Y; i++)
	{
		for (int j = 0; j < X; j++)
		{
			chess[i][j] = 0;
		}
	}
	printf("请输入马的初始位置:");
	int x, y;
	scanf("%d,%d", &x, &y);
	if (!TravelChess(x, y, 1))
		printf("马踏棋盘失败了,请更换起始位置!\n");
	clock_t end = clock();
	printf("本次运行一共用时:%fs", (double)(end - start) / CLOCKS_PER_SEC);

	return 0;
}

效率提高了不止一点。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/junya_zhang/article/details/83502416
今日推荐