审题:
本题需要我们找出将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
算法题(113):八数码难题
于 2025-04-01 23:29:39 首次发布