题目描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入格式
输入初始状态,一行九个数字,空格用0表示
输出格式
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
输入输出样例
输入 #1复制
283104765
输出 #1复制
4
A*:链接https://www.jianshu.com/p/e52d856e7d48
思路:
与传统的bfs不同的是,A算法根据要遍历的节点中提取信息选则最优的路而避免了大量无用的遍历,而bfs则无脑全部遍历了一遍。一般用优先队列完成,A算法难就难在f=g+h函数与实际问题关系的转化。
代码:
#include<bits/stdc++.h>
using namespace std;
string goal="123804765";
string str;
map<string,int>mp;
map<string,int>dis;
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
struct node{
int f,step;
string now1;
node(int a,int b,string c):f(a),step(b),now1(c){}
bool operator<(const node &a)const{
return f>a.f;
}
};
priority_queue<node> st;
int h(string str){//两个状态中不相同状态的数量
int cnt=0;
for(int i=0;i<9;i++){
if(goal[i]!=0&&goal[i]!=str[i])cnt++;
}
return cnt;
}
bool inside(int x,int y){
return (x>=1&&x<=3&&y>=1&&y<=3);
}
int A_star(){
while(!st.empty()){
node now=st.top();
string _new=now.now1;
st.pop();
if(now.now1==goal){
return now.step;
}
int fx,fy;
for(int i=0;i<9;i++){
if(now.now1[i]-'0'==0){
fx=i/3+1;fy=i%3+1;//取0对应行列
}
}
int t1=(fx-1)*3+fy-1;//行列对应的一维数组编号 ,(父节点)
for(int i=0;i<4;i++){
int fxx=fx+dir[i][0];
int fyy=fy+dir[i][1];
int t2=(fxx-1)*3+fyy-1;//行列对应的一维数组编号 ,(新节点)
if(inside(fxx,fyy)){
swap(_new[t1],_new[t2]);
if((mp[_new]==0)||(mp[_new]==1&&(dis[_new]>dis[now.now1]+1))){
dis[_new]=dis[now.now1]+1;
mp[_new]=1;
st.push(node(h(_new)+now.step+1,now.step+1,_new));//取(父节点步数和到目标状态步数之和)最小值,不能只考虑一种因素。 假设左上角与目标状态只差了一个状态,而右边与目标状态差了两个状态,但是目标状态就在右边的右边,即右边到目标状态位移:1+2,而左上角:1+x(x>2),所以因素有两个要考虑缺一不可
}
swap(_new[t1],_new[t2]);
}
}
}
}
int main(){
cin>>str;
if(h(str)==0){
cout<<"0"<<endl;
return 0;
}
st.push(node(h(str),0,str));
mp[str]=1;
dis[str]=0;
int ans=A_star();
cout<<ans<<endl;
}