【双向BFS】蓝桥杯试题 九宫重排 和 洛谷P1379 八数码难题

题目描述

OJ地址 http://lx.lanqiao.cn/problem.page?gpid=T42

如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
在这里插入图片描述
在这里插入图片描述
  我们把第一个图的局面记为:12345678.
  把第二个图的局面记为:123.46758
  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
  本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

输入:

输入第一行包含九宫的初态,第二行包含九宫的终态。

输出:

输出最少的步数,如果不存在方案,则输出-1。

样例输入1:

12345678.
123.46758

样例输出1:

3

样例输入2:

13524678.
46758123.

样例输出2:

22

思路:

最短路问题,一般都用BFS来解,难点在于每个状态的确定,借着这个题可以很好的理解一下BFS的思想。在BFS当中,队列里的东西实际上是是每个“状态”,当状态达到了题目所说的终点“状态”时,就算是找到答案了。这个题的状态很明显是九宫格内数字的位置,所以,struct内要放入的东西就是当前九宫格的数字排列。 知道了这个点就比较好解题了。

我在结构体内部定义了一个二维数组,存储的就是当前的九宫格,还有一个方法是取出当前九宫格状态到string类型。另一个方法getpoint() 用于找到当前九宫格内 " . "的坐标。

朴素BFS代码

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

map<string,bool> vis;

int dir[4][2] = {
    
    {
    
    -1,0},{
    
    0,-1},{
    
    1,0},{
    
    0,1}};

struct nn{
    
    
	int d;
	char m[3][3];
	
	string getstr(){
    
      // 取出当前的局面 二维数组到string   用于比对目标局面 
		string t= "";
		for(int i = 0; i < 9; i++){
    
    
		 	t += m[i/3][i%3];
		}
		return t;
	}
	
	// 返回当前的.坐标
	pair<int,int> getpoint(){
    
    
		for(int i = 0; i < 3; i++){
    
    
			for(int j = 0; j < 3; j++){
    
    
				if(m[i][j] == '.'){
    
    
					return make_pair(i,j);
				}
			}
		}
	} 
} node;

int main() {
    
    
	
	string sc,so;
	cin >> sc;
	cin >> so;
	for(int i = 0; i < 9; i++){
    
    
		node.m[i/3][i%3] = sc[i];
	}
	node.d = 0;
	queue<nn> q;
	q.push(node);
	
	while(!q.empty()){
    
    
		
		for(int i = 0;i < 4; i++){
    
    
			
			node = q.front();
			node.d = q.front().d + 1;

			int x,y;   // 取空格点坐标 
			x = node.getpoint().first;
			y = node.getpoint().second;
			
			int tx = x + dir[i][0];
			int ty = y + dir[i][1];
			
			if( tx < 0 || tx >= 3 || ty < 0 || ty >= 3){
    
      //出界 
				continue;
			}
			
			// 开始交换

			node.m[x][y] = node.m[tx][ty];
			node.m[tx][ty] = '.';

			//交换以后判断是不是目标局面 
			
			if(node.getstr() == so){
    
    
				cout << node.d << endl;
				return 0;
			}

			if(!vis[node.getstr()]){
    
     // 交换后的局面没出现过   标记一下,然后入队。 
				q.push(node);
				vis[node.getstr()] = true;
			}
			
		}
		q.pop();
	}
	
	cout << -1 << endl; 
	
	return 0;
}
CPU使用	609ms
内存使用	24.97MB

下面来看双向BFS,网上好多讲BFS的文章中的代码,有的变量复用了,读起来不是很容易理解,下边的代码个人感觉比较好理解,大致思路就是:两个队列q1 q2,q1从前往后扩展,q2从后往前扩展,每次扩展的时候,到底是扩展q1还是q2,就看这两个队列中哪个状态少,哪个少就扩展哪个,如果扩展的时候发现另一个队列已经扩展过这个状态了,那就是走通了,直接把双向走的步数相加就是结果。

双向BFS代码

#include <bits/stdc++.h>

using namespace std;
int dir[4][2] = {
    
    {
    
    -1,0},{
    
    0,-1},{
    
    1,0},{
    
    0,1}};
map<string,int> vis1, vis2; 

struct Node{
    
    
	string str;
	int step;
	Node(){
    
    
		step = 0;
		str = "";
	}
	
	// 返回.的位置 
	int getZero(){
    
    
		for(int i = 0; i < str.size(); i++){
    
    
			if(str[i] == '.'){
    
    
				return i;
			}
		}
	}
	
} node;

bool check(int x, int y){
    
    
	return x >= 0 and x < 3 and y >= 0 and y < 3;
}

int main(int argc, const char* argv[]){
    
    
	
	string sc,so;
	cin >> sc >> so;
	
	queue<Node> q1,q2;   //q1 正向 q2 反向 
	node.str = sc;
	node.step = 0;
	q1.push(node);
	
	node.str = so;
	q2.push(node); 
	
	while(!q1.empty() and !q2.empty()){
    
    
		
		int cur = 0;   // 扩展哪个队列 1 前向 2 反向 
		// 哪个少 扩哪个
		if(q1.size() < q2.size()){
    
    
			cur = 1;
			node = q1.front();
		} else{
    
    
			cur = 2;
			node = q2.front();
		}
		
		
		for(int i = 0; i < 4; i++){
    
    
			
			int zero = node.getZero();
			int x = zero / 3;
			int y = zero % 3; 
			
			int dx = x + dir[i][0];
			int dy = y + dir[i][1];

			if(!check(dx, dy)){
    
    
				continue;
			}
			// 交换两个位置的字符
			int aim = dx * 3 + dy;
			node.str[zero] = node.str[aim];
			node.str[aim] = '.';
			node.step++;
			
			
			// 交换结束 入队
			
			if(cur == 1){
    
    
				if(vis2[node.str] != 0){
    
    
					cout << node.step + vis2[node.str] << endl;
					return 0;
				}
			}else{
    
    
				if(vis1[node.str] != 0){
    
    
					cout << node.step + vis1[node.str] << endl;
					return 0;
				}
			}
			
			
			
			if(cur == 1){
    
    
				
				if(vis1[node.str] == 0){
    
    
					vis1[node.str] = node.step;
					q1.push(node);
				}
				
				node = q1.front();
			}else{
    
    
				if(vis2[node.str] == 0){
    
    
					vis2[node.str] = node.step;
					q2.push(node);
				}
				
				node = q2.front();
			}
			
		}
			
		if(cur == 1){
    
    
			q1.pop();
		}else{
    
    
			q2.pop();
		} 
	} 
	
	cout << -1 << endl;
	return 0;
}
CPU使用	15ms
内存使用	5.062MB

可见,双向BFS的效率相对于朴素BFS来说还是很高的,数据量越大,效果越明显,下边有一道类似的题目。

洛谷P1379 八数码难题

https://www.luogu.com.cn/problem/P1379

朴素BFS

#include <bits/stdc++.h>

using namespace std;
int dir[4][2] = {
    
    {
    
    -1,0},{
    
    0,-1},{
    
    1,0},{
    
    0,1}};
map<string,int> vis; 

struct Node{
    
    
	string str;
	int step;
	Node(){
    
    
		step = 0;
		str = "";
	}
	
	// 返回0的位置 
	int getZero(){
    
    
		for(int i = 0; i < str.size(); i++){
    
    
			if(str[i] == '0'){
    
    
				return i;
			}
		}
	}
	
} node;

bool check(int x, int y){
    
    
	return x >= 0 and x < 3 and y >= 0 and y < 3;
}

int main(int argc, const char* argv[]){
    
    
	
	string sc;
	cin >> sc;
	
	queue<Node> q1;
	node.str = sc;
	node.step = 0;
	q1.push(node);
	
	while(!q1.empty()){
    
    

		for(int i = 0; i < 4; i++){
    
    
			
			node = q1.front();
			
			if(node.str == "123804765"){
    
    
				cout << node.step << endl;
				return 0;
			}
			
			int zero = node.getZero();
			int x = zero / 3;
			int y = zero % 3; 
			
			int dx = x + dir[i][0];
			int dy = y + dir[i][1];

			if(!check(dx, dy)){
    
    
				continue;
			}
			// 交换两个位置的字符
			int aim = dx * 3 + dy;
			node.str[zero] = node.str[aim];
			node.str[aim] = '0';
			node.step++;
			
			
			// 交换结束 入队
			if(vis[node.str] != 0){
    
    
				continue;
			} 
			vis[node.str] = node.step;
			q1.push(node);
			
	
		}
			
		q1.pop();
	} 
	
	
	return 0;
}
编程语言
C++
代码长度
1.20KB
用时
13.51s
内存
14.59MB

双向BFS

#include <bits/stdc++.h>

using namespace std;
int dir[4][2] = {
    
    {
    
    -1,0},{
    
    0,-1},{
    
    1,0},{
    
    0,1}};
map<string,int> vis1, vis2; 
const string END = "123804765";

struct Node{
    
    
	string str;
	int step;
	Node(){
    
    
		step = 0;
		str = "";
	}
	
	// 返回0的位置 
	int getZero(){
    
    
		for(int i = 0; i < str.size(); i++){
    
    
			if(str[i] == '0'){
    
    
				return i;
			}
		}
	}
	
} node;

bool check(int x, int y){
    
    
	return x >= 0 and x < 3 and y >= 0 and y < 3;
}

int main(int argc, const char* argv[]){
    
    
	
	string sc;
	cin >> sc;
	
	if(sc == END){
    
    
		cout << 0 << endl;
		return 0;
	}
	
	queue<Node> q1,q2;   //q1 正向 q2 反向 
	node.str = sc;
	node.step = 0;
	q1.push(node);
	
	node.str = END;
	q2.push(node); 
	
	while(!q1.empty() and !q2.empty()){
    
    
		
		int cur = 0;   // 扩展哪个队列 1 前向 2 反向 
		// 哪个少 扩哪个
		if(q1.size() < q2.size()){
    
    
			cur = 1;
			node = q1.front();
		} else{
    
    
			cur = 2;
			node = q2.front();
		}
		
		
		for(int i = 0; i < 4; i++){
    
    
			
			int zero = node.getZero();
			int x = zero / 3;
			int y = zero % 3; 
			
			int dx = x + dir[i][0];
			int dy = y + dir[i][1];

			if(!check(dx, dy)){
    
    
				continue;
			}
			// 交换两个位置的字符
			int aim = dx * 3 + dy;
			node.str[zero] = node.str[aim];
			node.str[aim] = '0';
			node.step++;
			
			
			// 交换结束 入队
			
			if(cur == 1){
    
    
				if(vis2[node.str] != 0){
    
    
					cout << node.step + vis2[node.str] << endl;
					return 0;
				}
			}else{
    
    
				if(vis1[node.str] != 0){
    
    
					cout << node.step + vis1[node.str] << endl;
					return 0;
				}
			}
			
			
			
			if(cur == 1){
    
    
				
				if(vis1[node.str] == 0){
    
    
					vis1[node.str] = node.step;
					q1.push(node);
				}
				
				node = q1.front();
			}else{
    
    
				if(vis2[node.str] == 0){
    
    
					vis2[node.str] = node.step;
					q2.push(node);
				}
				
				node = q2.front();
			}
			
		}
			
		if(cur == 1){
    
    
			q1.pop();
		}else{
    
    
			q2.pop();
		} 
	} 
	
	
	return 0;
}
编程语言
C++
代码长度
1.91KB
用时
1.43s
内存
1.80MB

猜你喜欢

转载自blog.csdn.net/u014117943/article/details/104261195