思路
思路看起来可能有点乱,不好理解,但是看代码时就会豁然开朗
证明以及细节
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
#define x first
#define y second
using namespace std;
typedef pair<int,string> PIS;
int f(string m)//估计函数
{
int dt=0;
for(int i=0;i<9;i++)//这里1~8对应的下标为0~7
if(m[i]!='x')
{
int t=m[i]-'1';//对应下标
dt=dt+abs(i/3-t/3)+abs(i%3-t%3);//曼哈顿距离
}
return dt;//返回总曼哈顿距离
}
string bfs(string start)
{
string end="12345678x";//终点
unordered_map<string,int> d;//存储距离
priority_queue<PIS, vector<PIS>, greater<PIS>> heap;//小根堆,将元素的估计终点距离从小到大排序
unordered_map<string,pair<string,char>> last;//存储一个元素由哪种状态,经过哪种操作得来,跟前面几题一样
heap.push({f(start),start});//加入起点
d[start]=0;//起点到起点的距离为0
//要将操作数组与坐标变化数组一一对应
char oper[]="udlr";
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
while(heap.size())
{
auto t=heap.top();//队头
heap.pop();//弹出
string state=t.y;//记录
if(t.y==end) break;//终点出列的话就退出
int x,y;//查找x的横纵坐标
for(int i=0;i<9;i++)
if(state[i]=='x')
{
x=i/3,y=i%3;
break;
}
string init=state;
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) continue;//越界就跳过
swap(state[a*3+b],state[x*3+y]);//交换下标位置
if(!d.count(state)||d[state]>d[init]+1)//如果没有被记录或者小于记录值
{
d[state]=d[init]+1;//更新距离
heap.push({f(state)+d[state],state});//加入堆中
last[state]={init,oper[i]};//标记由哪种状态转移而来,并且记录执行的操作
}
state=init;//因为要扩展到四个方向,所以要还原
}
}
string ans;
//跟前面几题原来相同
while(end!=start)
{
ans+=last[end].y;
end=last[end].x;
}
reverse(ans.begin(),ans.end());//将其反转
return ans;
}
int main()
{
string start,x,c;
while(cin>>c)//这样输入可以忽视空格
{
start+=c;
if(c!="x") x+=c;
}
int res=0;//统计逆序对的数量
for(int i=0;i<8;i++)
for(int j=i+1;j<8;j++)
if(x[i]>x[j])
res++;
if(res%2) printf("unsolvable\n");//如果逆序对为奇数,就不可能抵达终点
else cout<<bfs(start)<<endl;//输出答案
return 0;
}
记录美好生活
爱你
想问一下,常规的A* 算法有两个集合,一个open set存储 待扩展的节点,一个close set 存储 已经扩展过的集合,但代码中好像只有open set 即 heap,是因为存入heap 前做了判断dist[state] > dist[source] + 1,从而保证了已经扩展的节点不会再进入 heap 中 而不需要close set 了吗?
还有就是证明的第一部分中最后一步实际距离大于估计距离没毛病吧,因为本来我们是往小的估,所以不能够推出矛盾吧?
矛盾点是dist肯定有一个u的扩展,但是推出来一个比u还小的扩展,所以矛盾
哦原来如此,感谢佬
佬,我看您只证明了逆序对为偶数是有解的必要条件,请问充分性如何保证呢?
这里为什么是大于原状态 + 1才转移呢
d[state]>d[init]+1
因为八数码问题本质还可以看成bfs吧,bfs相邻两层的距离就是1吧
我也搞不清楚 兄弟你想明白了吗
这里的1就好比dijkstra中的 dist[j] > dist[t] + g[t][j], 转移的状态差的步数就是1步
明白了谢谢兄弟
互帮互助
为什么估计函数是曼哈顿距离呢?
经过观察可以发现,每次移动只能把一个数字与空格交换位置,这样至多把一个数字向它在目标状态中的位置移近一步。即使每一步移动都是有意义的,从任何一个状态到目标状态的移动步数也不可能小于所有数字当前位置与目标位置的曼哈顿距离之和。----《算法竞赛进阶指南》
牛逼
真牛逼!!!