搜索二·骑士问题

描述

国际象棋棋盘上有3个骑士,能否通过若干次移动走到一起。要选择一个位置汇合,使得3个骑士行动的总次数最少?

输入

第1行:1个正整数t,表示数据组数,2≤t≤10。

第2..t+1行:用空格隔开的3个坐标, 每个坐标由2个字符AB组成,A为’A’~’H’的大写字母,B为’1’~’8’的数字,表示3个棋子的初始位置。

输出

第1..t行:每行1个数字,第i行表示第i组数据中3个棋子移动到同一格的最小行动步数。

样例输入

2
A1 A1 A1
B2 D3 F4

样例输出

0
2
解法一:搜索三个位置到所有位置的最小步数,再枚举所有位置,统计三个步数的和,求最小值。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;

char start[3][3];//表示三个骑士起始位置坐标字符表示
int step[3][8][8];//表示第i个骑士移动到(x,y)的最少步数
queue<pair<int, int>> q;//用于广度搜索的队列
int now_x, now_y;
int next_x, next_y;
int next_step[8][2] = {{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};

pair<int, int> move(int x, int y, int i)
{
	x += next_step[i][0];
	y += next_step[i][1];
	return make_pair(x, y);
}

bool checkIsInChessboard(int x, int y)
{
	if ((x >= 0 && x <= 7) && (y >= 0 && y <= 7))
		return true;
	return false;
}

void bfs_solve(int s[][8] ,int x, int y)
{
	//memset(s,-1,sizeof(s));
	//初始化为-1,表示没有访问过,但是有问题,赋值为0,结果减1
	//memset(s, 0, sizeof(s));为用这个就啥不行啊。。。
	for (int i = 0; i < 8; ++i)
		for (int j = 0; j < 8; ++j)
			s[i][j] = -1;

	//清空队列
	while (!q.empty())
		q.pop();

	s[x][y] = 0;//起始点,记为1
	q.push(make_pair(x,y));
	while (!(q.empty()))
	{
		//出队列
		pair<int, int> tp = q.front();
		now_x = tp.first;
		now_y = tp.second ;
		q.pop();
		for (int i = 0; i < 8; ++i)
		{
			pair<int, int> tp = move(now_x, now_y, i);
			next_x = tp.first;
			next_y = tp.second;

			if (checkIsInChessboard(next_x, next_y) && s[next_x][next_y] == -1)
			{
				s[next_x][next_y] = s[now_x][now_y] + 1;
				q.push(make_pair(next_x, next_y));
			}
		}
	}
}

int sumOfStep(int i, int j)
{
	int k;
	int sum = 0;
	for (k = 0; k < 3; ++k)
	{
		sum += step[k][i][j];
	}
	return sum;
}

int solve()
{
	int i, j;
	for (i = 0; i < 3; ++i)
	{
		bfs_solve(step[i],start[i][0]-'A',start[i][1]-'1');
	}
	int ans = 0x7FFFFFFF;//INFINITY;
	for (i = 0; i < 8; ++i)
		for (j = 0; j < 8; ++j)
		{
			int sum = sumOfStep(i, j);
			if (ans > sum)
			{
				ans = sum;
			}
		}
	return ans;
}

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		cin >> start[0] >> start[1] >> start[2];
		cout << solve() << endl;
	}
    return 0;
}
解法二:将开始输入的三个位置转换为一个八进制数,作为一种初始状态,枚举三个位置加八个方向,共8^6个状态,若出现第一个符合条件的即为最小结果,已经通过了,开始move函数写的有问题,其实状态的改变是根据坐标来的,虽然值在正常范围内,坐标还是会出界,所以要改成判断坐标的方式来检查是否合法。还有就是checkOK方法写错了,忘了加两边的括号,导致总是返回false。
<pre name="code" class="cpp">#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;

char start[3][3];//表示三个骑士起始位置坐标字符表示
queue<int> q;//用于广度搜索的队列
int step[270000];//记录8^6个状态整数
int now_status;//将当前位置坐标映射成一个八进制数1-8 => 0-7,A-H => 0-7
int next_status;

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

//将当前状态转移
//i表示第几个位置,j表示移动方向
//同时检查状态是否合法
int move(int status,int i, int j)
{
	int now_site = (status >> (6 * (2 - i))) & 0x3F;
	int now_x = now_site >> 3;
	int now_y = now_site & 0x7;

	int next_site = now_site;
	int next_x = now_x - next_step[j][1];
	int next_y = now_y + next_step[j][0];

	//if (next_site >= 0 && next_site <= 0x3F)
		//next_status = status - (now_site << (6 * (2 - i))) + (next_site << (6 * (2 - i)));//修改对应位置的状态
	if ((next_x >= 0 && next_x <= 7) && (next_y >= 0 && next_y <= 7))
	{
		next_site += next_step[j][0] - 8 * next_step[j][1];//注意此处的变化
		next_status = status - (now_site << (6 * (2 - i))) + (next_site << (6 * (2 - i)));//修改对应位置的状态
	}
	else
		return -1;
	return next_status;
}

bool checkOK(int status)
{
	//若三个位置相同,则找到一个结果
	return (((status & 0x3F) == ((status >> 6) & 0x3F)) && ((status >> 6) & 0x3F) == ((status >> 12) & 0x3F));
}

int solve()
{
	char s[7]="";
	start[0][0] = start[0][0] - 'A' + '0';
	start[1][0] = start[1][0] - 'A' + '0';
	start[2][0] = start[2][0] - 'A' + '0';

	start[0][1] = start[0][1] - '1' + '0';
	start[1][1] = start[1][1] - '1' + '0';
	start[2][1] = start[2][1] - '1' + '0';
	
	strcat(s, start[0]);
	strcat(s, start[1]);
	strcat(s, start[2]);
	
	//cout << s << endl;
	//cout  << strtol(s,NULL,8)<< endl;
	
	now_status = strtol(s, NULL, 8);//按八进制转换为十进制整数

	if (checkOK(now_status))
	{
		return 0;//开始状态符合,返回0
	}

	memset(step,-1,sizeof(step));//按字节初始化,只能为0或-1
	step[now_status] = 0;
	//清空队列
	while (!q.empty())
		q.pop();

	q.push(now_status);
	while (!q.empty())
	{
		now_status = q.front();
		q.pop();
		for (int i = 0; i < 3; ++i)
			for (int j = 0; j < 8; ++j)
			{
				next_status = move(now_status, i, j);
				
				if (next_status != -1 && step[next_status] == -1)
				{
					step[next_status] = step[now_status] + 1;
					q.push(next_status);
					if (checkOK(next_status))
					{
						return step[next_status];
					}
				}
			}
	}
	return 0;
}

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		cin >> start[0] >> start[1] >> start[2];
		cout << solve() << endl;
	}
	return 0;
}
    原文作者:骑士周游问题
    原文地址: https://blog.csdn.net/Tramp_1/article/details/51487862
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞