c++:中国象棋中的跳马问题

问题 E: 中国象棋中的跳马问题

题目描述

现在棋盘的大小不一定,由p,q给出,并且在棋盘中将出现障碍物(限制马的行动,与象棋走法相同) 输入 第一行输入n表示有n组测试数据。每组测试数据第一行输入2个整数p,q,表示棋盘的大小(1<=p,q<=100)。 每组测试数据第二行输入4个整数,表示马的起点位置与终点位置。(位置的取值范围同p,q) 第三行输入m表示图中有多少障碍。 接着跟着m行,表示障碍的坐标。

输出

马从起点走到终点所需的最小步数。 如果马走不到终点,则输入“can not reach!”

样例输入 Copy

2
9 10
1 1 2 3
0
9 10
1 1 2 3
8
1 2
2 2
3 3
3 4
1 4
3 2
2 4
1 3

样例输出 Copy

1
can not reach!

提示

此题是一个搜索题,可用DFS或BFS,建议选择BFS(广搜)。一开始把马的起始点加入队列,然后用广搜的思想把此点能到达的其他点加入队列,这里需要一个数组用来记录此点在之前是否已经加入队列,如果加入过队列当中,就不需要再加入了,直到队列里的元素为空,或者搜索到了终点,搜索即停止,然后输出相应答案即可。

思路:
1、输入样例个数n,用while实现个数。
2、输入棋盘大小。
3、输入起点与终点,设置障碍坐标。
4、使用广度优先搜索,队列,初始化一个数组代表8个方向。
5、最小步数:遍历完每一层num_foot++。
6、走不到终点:队列为空,输出 “can not reach!”。

实现代码:

#include<bits/stdc++.h>
using namespace std;

//输出chess数组的函数,可以测试用
// void print_chess(int *chess, int w, int h){
// 	cout << "---------------------------------------------------" << endl;
// 	string show[] = {" .", " +", " 0"};
// 	for (int i = 0; i <= w; ++i)
// 	{
// 		cout << " " << (i%10);

// 	}
// 	cout << endl;
// 	for (int i = 1; i <= w; ++i)
// 	{
// 		cout << (" ") << (i%10);
// 		for (int j = 1; j <= h; ++j)
// 		{
// 			cout << show[ chess[i*(w+1)+j] ];
// 		}
// 		cout << endl;
// 	}
// }

int main(int argc, char const *argv[])
{
	int n;
	cin >> n;   // 1、样例个数n

	whi:
	while(n--)
	{
		int w,h;
		cin >> w >> h;    // 2、棋盘的大小w,h
		int base = max(w,h) + 1;   /*防止取余和取模时候有歧义,首先要取 w,h 的最大值,再+1,(举例)*/

		int chess[w+1][h+1] = {0};    // chess二维数组赋值为0
		for (int i = 0; i <= w; ++i)
		{
			for (int j = 0; j <= h; ++j)
			{
				chess[i][j] = 0;        /*初始化问题*/
			}
		}

		int x1,y1,x2,y2;  //3、  start = (x1,y1) ,end = (x2,y2)
		cin >> x1 >> y1 >> x2 >> y2 ; 

		int num_barr;   //障碍个数
		cin >> num_barr;
		while(num_barr--)
		{
			int xb,yb;
			cin >> xb >> yb ;
			chess[xb][yb] = 2;  //在棋盘中,障碍的坐标赋值为2
		}

		int start = x1 *base + y1;  //把坐标储存在一个数当中
		int end = x2 * base + y2;

		int num_foot = 0;
		queue <int > q;
		q.push(start);
		chess[x1][y1] = 1;

		while(q.size())  //4、广度优先搜索
		{
			int queue_size = q.size();
			while(queue_size--)   //5、遍历每一层
			{

				int front = q.front() ;  q.pop();
				int x = front / base ;
				int y = front % base ;

				if(x == end/base && y == end%base)
				{
					cout << num_foot << endl;
					goto whi;
				}

				int _x[8] = {2, 2, 1, -1, -2, -2, -1, 1};
				int _y[8] = {1, -1, -2, -2, -1, 1, 2, 2};

				for (int i = 0; i < 8; ++i)
				{
					int next_x = x + _x[i];
					int next_y = y + _y[i];

					int majiao_x = x + _x[i]/2;
					int majiao_y = y + _y[i]/2;

					if ( next_x>=1 && next_x<=w &&
						 next_y>=1 && next_y<=h &&
						 chess[majiao_x][majiao_y] != 2 &&
						 chess[next_x][next_y] == 0
						){

						chess[next_x][next_y] = 1;
						q.push(next_x * base + next_y);
					}
				}
			}
			num_foot++;  
		}
		// if(q.size()==0)
		cout << "can not reach!" << endl;  //6、队列为空,即把所有能走的都访问过,如果还到不了终点,即输出
	}
	return 0;
}

总结:

  1. 读完题知道这是一个广度优先搜索,常规需要 :存储题目所给内容的数组、vis[]数组、队列进行遍历等。而这道题我把数组合成一个chess[]数组,先进行初始化全部为0,如果访问过则改成1,障碍则为2,这样就可以减少数组的使用。
  2. 数组的初始化问题,最开始写的是int chess[w][h] = {0},这样其实没有很好的初始化,所以需要用两个for循环进行赋值,或者使用memset()函数也可。
  3. 题目中棋盘的大小p,q是从1开始的,要认真读题!!
  4. 运用取余和取模运算时,乘的n要是比较大的数,不然会有歧义。例:w=3,h=10,当坐标为(4,7)时,通过取余取模运算后坐标就变成了(6,1),因此可知乘的数要是w,h中较大的数,还要加一,不然也会有歧义。同样的道理,在从front取余取模变成坐标的时候,也应该是max(w,h)+1,我在最开始定义为了base,方便后边计算。
  5. 这道题是遍历完一层,只要没到达终点,步数(num_foot)就要加一,同样用到了一个巧妙的queue_size,因为每次入队是把下一层所有入队,再进行while循环,就可以遍历每一层之后num_foot++。
  6. 队列为空,即把所有能走的都访问过,如果还到不了终点,即输出can not reach!。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值