原题链接:
AcWing:AcWing3156.卡片换位
蓝桥杯官网:卡片换位
题目描述:
给一张 2 * 3 的图,上面有 'A' 跟 'B' ,还有一个空格可以上下左右走,问最少需要多少步可以使 'A' 跟 'B' 的位置互换(其他位置无所谓)
思路来源:
一个二维图的状态表示与存储当然麻烦,我们可以将其转化为一个一维的字符串,使用时再将其转换为二维图的坐标。
具体:
① 使用字符串 string 存储状态,队列 queue<string>
② 使用哈希表 unordered_map<string, int> 标记当前状态是否计算,并记录最少需要多少步可以得到这种状态
③题目中说可以移动到相邻的空格上,实际上就是空格在走
代码实现:
如果看这个代码块看的不爽的可以去最下面看AcWing代码的图片
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map> //哈希表头文件
#include <queue> //队列
using namespace std;
//返回使 'A' 'B' 换位需要的最少步数
int bfs(string start)
{
//初始状态时 'A' 与 'B' 的位置
int A = start.find('A'), B = start.find('B');
// 4 方向偏移量
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
//哈希表,string 为当前状态,int 为走出这种状态的最少步数
unordered_map<string, int> d;
//队列
queue<string> q;
//标记初始状态步数为 0
d[start] = 0;
q.push(start); //入队
//循环条件:队列为空的时候终止
// while(q.size()) 或 while(!q.empty())
while (q.size())
{
//取出队头
auto t = q.front();
q.pop(); //弹出
// dist 为走出 t 的最少步数
int dist = d[t];
// a 是当前状态 'A' 的位置(下标),b 同理
int a = t.find('A'), b = t.find('B');
//如果当前 'A' 的位置是最开始时 'B' 的位置,'B' 的是 'A' 的(B <=> A)
//其他字符位置无所谓,只考虑 'A' 'B' 是否互换
//说明找到了,返回步数
if (a == B && b == A) return dist;
//找到当前空格的位置
int k = t.find(' ');
//转换为二维图的 x y 坐标
int x = k % 3, y = k / 3;
//遍历 4 方向偏移量
for (int i = 0; i < 4; i++)
{
//得到新的 x 与 y
int nx = x + dx[i], ny = y + dy[i];
//如果新的 x y 在合法的范围内(地图内)
if (nx >= 0 && nx <= 2 && ny >= 0 && ny <= 1)
{
//将空格与该位置上的数互换(相当于空格往那里走)
swap(t[k], t[ny * 3 + nx]);
//如果没有记录过这个状态
if (!d.count(t))
{
//标记这种状态,步数为父状态的步数加一
d[t] = dist + 1;
q.push(t); //入队
}
//还原状态
swap(t[k], t[ny * 3 + nx]);
}
}
}
//无法实现返回 -1
return -1;
}
int main()
{
string str; //存图
char ch; //因为有空格,所以用了 getchar()
//循环 7 次,图有 6 个字符,加上换行符一共 7 个
for (int i = 0; i < 7; i++)
{
ch = getchar();
//如果不是换行符,储存
if (ch != '\n') str += ch;
}
cout << bfs(str) << endl;
return 0;
}