算法题(113):八数码难题

审题:

本题需要我们找出将3*3矩阵从初始矩阵变为目标矩阵所需要的最短步数

时间复杂度:本题的数据量极小,所以可以允许绝大多数的算法

思路:
方法一:bfs

首先我们分析搜索方法:我们需要找到矩阵的字符0位置,然后对它进行上下左右四个方向的交换移动,移动完之后将新的矩阵放入queue中。

疑问1:如何表示矩阵?

我们可以使用字符串来表示矩阵,比如这里的初始矩阵就可以表示为“283104765”

疑问2:如何简单的模拟矩阵元素的交换?

假设矩阵为n*m的矩阵

(1)二维矩阵坐标转一维索引:

{x,y}--->i: i = m*x + y(基准为0)

(2)一维索引转二维矩阵

i---->{x,y}: x = i/m; y = i%m(基准为0)

疑问3:如何对重复数据进行剪枝?

我们用unordered_map<string,int>来记录变换到某个矩阵所需的最短次数,而string就是矩阵的字符串表示,int就是最短次数。当我们count哈希表时,若有值说明已经遍历过该状态,就剪枝该情况,直接跳过

解题:

(1)预处理

#include<iostream>
#include<unordered_map>
#include<queue>
#include<vector>
using namespace std;
string str;
string answer = "123804765";
unordered_map<string, int> m;
typedef pair<int, int> PII;
queue<string> q;// 存储待移动字符串
//方向数组
vector<vector<int>> d = {{1,0},{-1,0},{0,1},{0,-1}};
//二维转一维
int DR(PII pos)
{
	int a = pos.first * 3 + pos.second;
	return a;
}
// 一维转二维
PII PR(int pos)
{
	PII a;
	a.first = pos / 3;
	a.second = pos % 3;
	return a;
}

1.queue用于记录等待进行交换的新的矩阵,之所以不用queue存储0的坐标是因为0很容易搜索出来。

(2)main函数

int main()
{
	cin >> str;
	q.push(str);
	m[str] = 0;//将初始矩阵状态放入map
	bfs();
	cout << m[answer];
	return 0;
}

bfs作用是把到answer矩阵的最短步数计算出来

(3)bfs

//bfs搜索
void bfs()
{
	while (!q.empty())
	{
		int size = q.size();
		for (int i = 0; i < size; i++)
		{
			string s = q.front();
			q.pop();
			int pos1 = 0;//0字符索引
			while (s[pos1] != '0')
			{
				pos1++;
			}
			PII pos = PR(pos1);//0字符坐标
			for (auto& e : d)
			{
				int newx = pos.first + e[0];
				int newy = pos.second + e[1];
				//进行移动
				if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3)
				{
					int pos2 = DR({ newx,newy });
					string s1 = s;//防止s状态改变
					swap(s1[pos1], s1[pos2]);
					if (m.count(s1))
					{
						continue;//已经遍历过
					}
					else//新的情况
					{
						m[s1] = m[s]+1;
						if (s1 == answer){
							return;
						}
						else{
							q.push(s1);
						}
					}
				}
			}
		}
	}
}

整体逻辑:

1.先把0字符索引和待交换位置索引弄出来

2.交换对应字符

3.判断是否遍历过

若遍历过就跳过

没有就进一步判断新的字符串是否为answer,若是就返回

不是就将新的字符串插入队列中

注意:

1.为了防止把字符s给改变,我们需要先用s1去得到s的值,改变临时变量s1.

2.正规的bfs写法都是用pair类型去维护待查找对象和其最短路的,这里我们的当前最短路就是用前一个状态的最短路+1

P1379 八数码难题 - 洛谷

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值