最小步数问题(BFS)

引入

最小步数问题也是通过bfs求最短路的问题。
但之前我们遇到的bfs求最短路都是只有一张图,所有状态都在一个图内,例如走迷宫问题。BFS应用——走迷宫
最小步数问题每个状态都分别对应一张图
那么怎样表示这些状态就是一个难点。通常我们会借用哈希表来表示状态。

AcWing 845. 八数码

在这里插入图片描述
补充一个小技巧:
设元素x在一维数组的下标为index,二维数组的下标为(row,col),(ps:已知二维数组长度为n * m)
一维数组转换为二维数组的下标
row = index / m
col = index % m

二维数组转换为一维数组的下标
index = col + row * m

(可以发现,下标转换过程中只与列数m有关,与行数n无关)

#include<iostream>
#include<queue>
#include<string>
#include<unordered_map>
using namespace std;
int dx[4] = {0,-1,0,1}, dy[4] = {-1,0,1,0};

int bfs(string start)
{
	string end = "12345678x"; //终点状态
	
	queue<string> q;
	unordered_map<string,int> d; // 最小步数数组 
	
	q.push(start);
	d[start] = 0;
	
	while(q.size())
	{
		string t = q.front();
		q.pop();
		
		int distance = d[t];
		if(t == end) return distance; //当状态为终点状态时 
		
		int k = t.find('x'); //找到 x 在字符串t中的下标
		int x = k / 3, y = k % 3; //将 x 的一维数组的下标转换为3X3的二维数组下标
		
		for(int i=0; i<4; i++) //状态转移 
		{
			int a = x + dx[i], b = y + dy[i];
			if(a>=0 && a<3 && b>=0 && b<3) 
			{
				swap(t[k], t[a * 3 + b]); //将x与这个数进行交换(在一维字符串数组中交换)
				
				if(!d.count(t)) //新状态没有被搜过,更新距离并加入队列
				{
					d[t] = distance + 1;
					q.push(t);
				} 
				
				swap(t[k], t[a * 3 + b]); //恢复状态
			}
		} 
	}
	return -1;
}

int main()
{
    string start; //初始状态
	for(int i=0; i<9; i++)
	{
		char c;
		cin >> c;
		start += c;
	} 
	
	cout << bfs(start) << endl;
}

AcWing 1107. 魔板

在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<string>
#include<queue>
#include<unordered_map>
#include<algorithm>

using namespace std;
unordered_map<string,int> d; //最小步数数组
unordered_map<string,pair<char,string>> pre; //记录每个状态是由哪个状态转移的 
queue<string> q;

char g[2][4];

void set(string state) //将字符串按顺时针放回g数组中
{
	for(int i=0; i<4; i++) g[0][i] = state[i];
	for(int i=3,j=4; i>=0; i--,j++) g[1][i] = state[j];
} 

string get() //将g数组中字符按顺时针变成字符串 
{
	string res;
	for(int i=0; i<4; i++) res += g[0][i];
	for(int i=3; i>=0; i--) res += g[1][i];
	return res;
}

string move0(string state) //A操作 
{
	set(state);
	for(int i=0; i<4; i++) swap(g[0][i],g[1][i]);
	return get();
}

string move1(string state) //B操作 
{
	set(state);
	char v0 = g[0][3], v1 = g[1][3]; //将最后一列保存下来 
	for(int i=3; i>0; i--) //将前三列向右移 
	{
		g[0][i] = g[0][i-1]; 
		g[1][i] = g[1][i-1];
	} 
	g[0][0] = v0, g[1][0] = v1; //将最后一列赋到第一列上 
	return get();
}

string move2(string state) //C操作 
{
	set(state);
	char v = g[0][1]; //将左上角存下来
	g[0][1] = g[1][1];
	g[1][1] = g[1][2];
	g[1][2] = g[0][2];
	g[0][2] = v; 
	return get();
}

void bfs(string start,string end)
{
	q.push(start);
	d[start] = 0;
	
	while(q.size())
	{
		string t = q.front();
		q.pop();
		
		string m[3]; //A、B、C三种操作后得到的字符串
		m[0] = move0(t); //A操作
		m[1] = move1(t); //B操作
		m[2] = move2(t); //C操作
		
		for(int i=0; i<3; i++)
		{
			string s = m[i]; //某个操作后的字符串 
			if(!d.count(s)) //没有被搜过
			{
				d[s] = d[t] + 1;//更新距离
				pre[s] = {char(i+'A'),t};
				if(s == end) break;
				q.push(s);
			} 
		} 
	}
}

int main()
{
	int x;
	string start,end; //初始状态、终点状态
	for(int i=0; i<8; i++)
	{
		cin >> x;
		end += char(x + '0');
	} 
	
	for(int i=0; i<8; i++) start += char(i + '1');
	
	bfs(start,end);
	cout << d[end] << endl;
	
	string res;
	while(end != start)
	{
		res += pre[end].first;
		end = pre[end].second; //将end更新为上一个状态 
	}
    reverse(res.begin(),res.end()); //头文件algorithm
	
	if(res.size()) cout << res; 
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值