2022-11-22 Acwing每日一题

本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的。同时也希望文章能够让你有所收获,与君共勉!

八数码

在一个 3×3 的网格中,1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 3×3 的网格中。

例如:

1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x 与其上、下、左、右四个方向之一的数字交换(如果存在)。

我们的目的是通过交换,使得网格变为如下排列(称为正确排列):

1 2 3
4 5 6
7 8 x

例如,示例中图形就可以通过让 x 先后与右、下、右三个方向的数字交换成功得到正确排列。

交换过程如下:

1 2 3 > 1 2 3 > 1 2 3 > 1 2 3
x 4 6 > 4 x 6 > 4 5 6 > 4 5 6
7 5 8 > 7 5 8 > 7 x 8 > 7 8 x

现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。

输入格式
输入占一行,将 3×3 的初始网格描绘出来。

例如,如果初始网格如下所示:

1 2 3
x 4 6
7 5 8

则输入为:1 2 3 x 4 6 7 5 8

输出格式
输出占一行,包含一个整数,表示最少交换次数。

如果不存在解决方案,则输出 −1。

输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19

算法原理

将每一个状态视为一个节点,那么该题要求的就是从初始状态到末状态的最短路。在这道题里,我们将整个网格视为一种状态,那么我们该怎么来表示这个状态呢,既然我们要求最短路,我们来怎么表示他们的距离呢,我们又该用什么算法去搜索呢?好吧,肯定是BFS搜索了,我们可以使用一个字符串来表示整个表格(比较好的一点我们可以从这里学习到一维索引和二维索引我们是怎么转化),至于距离,我们可以使用哈希表来存储我们当前状态和距离目标还有多少距离,我们这里的距离定义为从初始状态到当前状态的操作数。那么现在来看看这道题的BFS的算法:

  1. 初始化队列和哈希表(队列里就存储我们的状态)
  2. 在循环里先取出队头的状态(别忘了删掉队头),判断当前状态是否是目标状态,如果是就直接返回当前操作数。
  3. 不是目标状态我们就要找出x的位置,并且对他的四个方向都进行搜索。
  4. 取得搜索的位置后,我们先判断是否出界,在进行状态转移,判断状态转移后的状态是否出现过,如果没出现过就加入队列里等待以后去扩展搜索这个状态。需要注意的是我们每一次进行状态转移时会对网格进行操作,因此为了不影响其他状态,我们需要在判断完新的状态后进行回溯
  5. 如果BFS对于网格的每一种状态都搜索过还没有解,说明无解,返回-1。

代码实现

#include <iostream>
#include <cstring>
#include <algorithm>

#include<queue>
#include<unordered_map>
using namespace std;

unordered_map<string,int> d;
queue<string> q;

int dx[] = {-1,1,0,0},dy[] = {0,0,-1,1};

int bfs(string state){
    string end = "12345678x";
    // 初始化状态和哈希表
	q.push(state);
	d[state] = 0;
	
	while(q.size()){
		auto t = q.front();
		q.pop();	// 队头变成已经搜索过的状态
		int dist = d[t];    // 该状态对应的操作数
		
		if(t == end)	return dist;
		
		int k = t.find('x');	// 找到x在字符串中的位置,返回的是他的下标
		int x = k / 3,y = k % 3;	// 一维数组中的位置索引转化成二维中的位置索引
		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[a*3+b],t[k]);	// 搜索位置于x的位置,状态转移操作
				if(!d.count(t))	// 表示操作过的状态没有出现过就加入该状态
				{
					q.push(t);
					d[t] = dist + 1;	// 上一个状态进行的操作数+1
				}
				swap(t[a*3+b],t[k]);	// 状态回溯,因为我们是直接对状态图进行改变,为了不影响其他状态,所以需要进行回溯
			}
		}
	}
	return -1;
}

int main(void){
	char s[2];
	string start = "";
	for(int i=0; i < 9 ; ++i){
		
		cin >> s;
		start += *s;
	} 
	cout << bfs(start) << endl;
	return 0;	
}
posted @ 2022-11-22 13:26  ZmQmZa  阅读(11)  评论(0编辑  收藏  举报