BFS + 压缩状态
首先,对于这种步数为1,求最短步数的问题,我们应该立马想到BFS求最短路
难点在于状态表示
我们可以把每一个二维状态(3×3表格)压缩并看成一个一维字符串
并把所有的状态转换看成一个图
起点就是输入的状态,终点就是答案要求的状态,从一个状态到另一个状态可以看作连出去一条有向边,那么这就是一个求最短路问题了。
例如下图(初态为X321)(从左到右,从上到下),我们可以发现,图中有冗余(重复出现)的节点,在画图的时候,我们可以画出来,但在程序中,我们应该尽力避免。
一维和二维数组坐标之间的转换
这里的数组下表都从0开始
一维数组位置k到二维数组(n行n列)的映射
x=k/n,y=k%n
二维数组(x,y)以为数组的映射
k=x*n+y
注意:我们二维数组规定为n行n列是有用意的,在二维->一维的转换中,行列的值必须相同!即便原来的数组只有两行三列,我们也要按三行三列去算!
下面的交换A和B的位置一题就是例子!
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
using namespace std;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs(string start)
{
unordered_map<string, int> d;//状态到距离的映射的哈希表
queue<string> q;
string end = "12345678x";
q.push(start);
while(q.size())
{
auto t = q.front();
q.pop();
int distance = d[t];
//如果到了答案状态
if(t == end) return distance;
//否则,找到空格的位置
int k = t.find('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 || b < 0 || a >= 3 || b >= 3) continue;
//交换‘x’和与它相邻字符的位置
swap(t[k], t[a * 3 + b]);//二维数组转换成一维数组
if(!d.count(t))//这个状态没有走过
{
d[t] = distance + 1;
q.push(t);
}
//加入队列之后还要换回来以供下一次使用
swap(t[k], t[a * 3 + b]);//二维数组转换成一维数组
}
}
return -1;//如果没找到符合的答案
}
int main()
{
ios::sync_with_stdio(false);
string start;
for(int i = 0; i < 9; i ++ )
{
char ch;
cin >> ch;
start += ch;
}
int res = bfs(start);
cout << res << endl;
return 0;
}
#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_map>
#include <queue>
using namespace std;
int aa, bb;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs(string start)
{
queue<string> q;
unordered_map<string, int> d;
q.push(start);
d[start] = 0;
while(q.size())
{
auto t = q.front();
q.pop();
int distance = d[t];
int k1 = t.find('A'), k2 = t.find('B');
if(k1 == bb && k2 == aa)
{
// cout << "res: " << t << endl;
return distance;
}
int k = t.find(' ');
int x = k / 3, y = k % 3;//即便是两行三列,我们也要除3,而不是除2!
for(int i = 0; i < 4; i ++ )
{
int xx = x + dx[i], yy = y + dy[i];
if(xx < 0 || yy < 0 || xx >= 2 || yy >= 3) continue;
swap(t[k], t[xx * 3 + yy]);
if(!d.count(t))
{
d[t] = distance + 1;
q.push(t);
}
swap(t[k], t[xx * 3 + yy]);
}
}
return -1;
}
int main()
{
ios::sync_with_stdio(false);
string start = "";
for(int i = 0; i < 2; i ++ )
{
string s;
getline(cin, s);
start += s;
}
// cout << "s:" << start << endl;
for(int i = 0; i < 6; i ++ )
{
if(start[i] == 'A') aa = i;
else if(start[i] == 'B') bb= i;
}
// cout << "a:b " << aa << ' ' << bb << endl;
int res = bfs(start);
cout << res << endl;
return 0;
}
在这个代码中,看似正确,但其实我犯了一个很大的错误
题目中要求的是A和B的位置互换即可
我却想当然的直接把A和B的位置直接互换了
但其实这样其他位置放置的东西就确定了
虽然这是一个答案,但这不是最小值
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
using namespace std;
int bfs(string start, string end)
{
unordered_map<string, int> d;
d[start] = 0;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
queue<string> q;
q.push(start);
while(q.size())
{
auto t = q.front();
q.pop();
int distance = d[t];
if(t == end) return distance;
int k = t.find(' ');
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 || b < 0 || a >= 2 || b >= 3) continue;
swap(t[k], t[a * 3 + b]);
if(!d.count(t))
{
q.push(t);
d[t] = distance + 1;
}
swap(t[k], t[a * 3 + b]);
}
}
return -1;
}
int main()
{
string start, end;
int aa, bb;
for(int i = 0; i < 2; i ++ )
{
string ts;
getline(cin, ts);
start += ts;
}
for(int i = 0; i < 6; i ++ )
{
if(start[i] == 'A') aa = i;
else if(start[i] == 'B') bb = i;
}
end = start;
swap(end[aa], end[bb]);
cout << start << "|||" << end << endl;
int res = bfs(start, end);
cout << res << endl;
return 0;
}
1
暴力写法:TLE
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <unordered_map>
#include <map>
#include <queue>
using namespace std;
string start, endstr;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
char way[4] = {'u', 'r', 'd', 'l'};
int bfs(string &pw)
{
unordered_map<string, int> d;
unordered_map<string, string> path;
queue<string> q;
q.push(start);
d[start] = 0;
while(q.size())
{
auto t = q.front();
q.pop();
string pathway = path[t];
int distance = d[t];
if(t == endstr)
{
// cout << path[t] << endl;
pw = pathway;
return distance;
}
int k = t.find('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 || b < 0 || a >= 3 || b >= 3) continue;
swap(t[k], t[a * 3 + b]);
if(!d.count(t))
{
q.push(t);
d[t] = distance + 1;
path[t] = pathway + way[i];
}
swap(t[k], t[a * 3 + b]);
}
}
return -1;
}
int main()
{
ios::sync_with_stdio(false);
int n; cin >> n;
for(int i = 1; i <= n; i ++ )
{
string path;
cin >> start >> endstr;
int res = bfs(path);
cout << "Case " << i << ": " << res << endl;
cout << path << endl;
}
return 0;
}
正解:IDA*
#include<bits/stdc++.h> using namespace std; using pis = pair<int, string>; int dx[] = {1, 0, 0, -1}, dy[] = {0, -1, 1, 0}; char d[] = {'d', 'l','r', 'u'};//按照字典序排列 char s[10], ed[10]; int dep, x, y; int edx[9], edy[9], path[100]; bool dfs(int u, int ida){ if(u + ida > dep) return 0;//剪枝1:现有的深度和估价出的深度要超过最大深度的话就直接return if(!strcmp(s, ed)){//如果 s == ed cout << u << '\n'; for(int i = 0; i < u; i++) cout << d[path[i]]; cout << '\n'; return 1; } int tmpx = x, tmpy = y; for(int i = 0; i < 4; i++){ if(u && path[u - 1] + i == 3) continue;//剪枝2:不走反方向 int nx = tmpx + dx[i], ny = tmpy + dy[i]; if(nx >= 0 && ny >= 0 && nx < 3 && ny < 3){ int a = s[nx * 3 + ny] - '1', ret = 0;//a:X即将走的位置的数字,ret:ida的改变量 ret += abs(tmpx - edx[a]) - abs(nx - edx[a]) + abs(tmpy - edy[a]) - abs(ny - edy[a]);// x = nx, y = ny, path[u] = i;//更新x,y,path swap(s[nx * 3 + ny], s[tmpx * 3 + tmpy]);//更新s if(dfs(u + 1, ida + ret)) return 1; swap(s[nx * 3 + ny], s[tmpx * 3 + tmpy]);//还原s } } x = tmpx, y = tmpy;//还原x,y,这个位置没写,找了2个小时 return 0; } int main(){ ios::sync_with_stdio(0); cin.tie(0); int tt; cin >> tt; for(int i = 1; i <= tt; i++){ cout << "Case " << i << ": "; for(int i = 0; i < 9; i++) cin >> s[i]; for(int i = 0; i < 9; i++) cin >> ed[i]; for(int i = 0; i < 9; i++){ if(ed[i] != 'X') edx[ed[i] - '1'] = i / 3, edy[ed[i] - '1'] = i % 3;//记录每一位的x,y值 if(s[i] == 'X') x = i / 3, y = i % 3;//初始化位置x,y } int ida = 0; for(int i = 0; i < 9; i++) if(s[i] != 'X') ida += abs(i / 3 - edx[s[i] - '1']) + abs(i % 3 - edy[s[i] - '1']); for(dep = 0; ;dep++) if(dfs(0, ida)) break; } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现