问题引入:在进行bfs搜索的过程中,只能说明起始状态距离该状态的代价最小,但是在未来的搜索中,该状态到目标状态的可能会花费更高的代价,导致最优解的搜索量增大。
为了提高搜索效率,可以让那些代价大的方案尽可能的在后面进行搜索,此时就需要引入A*算法。
做法:设计一个估值函数f(state),估算出当前距离目标状态的代价,使估值越小的状态,尽可能的在前面搜索。
实现方法:利用优先队列存储,值为当前状态的代价+估计的代价 即:d[state] + f(state)
估值函数的实现:可以将估值函数设计为:当前状态的所有数字的位置与目标状态的曼哈顿距离之和
//求曼哈顿距离的函数
int f(string s){
int res = 0;
for(int i = 0;i<n;i++){
if(s[i] == 'x') continue;
int a = s[i]-'1';
res += abs(i/3-a/3)+abs(a%3-i%3);
}
return res;
}
ac代码:
#include<iostream>
#include<queue>
#include<vector>
#include<map>
#include<cmath>
using namespace std;
typedef pair<int,string> PIS;
const int n = 9;
//定义小跟堆
priority_queue<PIS,vector<PIS>,greater<PIS> > q;
string anss = "12345678x";//目标状态
//求曼哈顿距离的函数
int f(string s){
int res = 0;
for(int i = 0;i<n;i++){
if(s[i] == 'x') continue;
int a = s[i]-'1';
res += abs(i/3-a/3)+abs(a%3-i%3);
}
return res;
}
//当前状态的具体步数
map<string,string> ma;
string bfs(string s){
int i;
//优先队列存入期望步数+实际步数,和具体方案
q.push({f(s)+0,s});
ma[s] = "";//第一次放入队列为空
int xx[] = {-1,1,0,0};//表示上下左右,udlr
int yy[] = {0,0,-1,1};
string dir = "udlr";
while(!q.empty()){
PIS t = q.top();
q.pop();
string s = t.second;
if(s == anss) return ma[s];
// if(ma[s][0] == 'u' && ma[s][1]=='l' && ma[s][2] == 'l' && ma[s][3] == 'd'&& ma[s][4] == 'd'&& ma[s][5]== 'r' && ma[s][6] == 'u' && ma[s][7] == 'r' && ma[s][8] == 'd'&& ma[s][9] == 'l' && ma[s][10] == 'l'&& ma[s][11] == 'u' && ma[s][12] == 'r')
// cout<<s<<" "<<ma[s]<<endl;
//算出x的位置
int x,y;
for(i = 0;i<=n;i++)
if(s[i] == 'x'){
x = i/3;y= i%3;
}
for(int i = 0;i<4;i++){
int x1 = x+xx[i],y1 = y+yy[i];
if(x1<0 || x1>2 || y1<0 || y1>2) continue;
string s1 = s;//赋值数组
swap(s1[x1*3+y1],s1[x*3+y]);
int dis = ma[s].length()+1;
//如果访问过,并且比当前距离还小,continue;
if(ma.count(s1) == 1 && dis>=ma[s1].size()) continue;
ma[s1] = ma[s]+dir[i];
//进队
q.push({dis + f(s1),s1});
}
}
return " ";
}
int main(){
int i,j;
string a;
string s;//初始状态
for(i = 1;i<=9;i++){
cin>>a;
s+=a;
}
//先求逆序对的数量,逆序对的数量为奇数则无解
int cnt = 0;
for(i =0;i<n-1;i++){
for(j = i+1;j<n;j++){
if(s[i] == 'x' || s[j] == 'x') continue;
if(s[i]>s[j]){
cnt++;
}
}
}
// cout<<cnt<<endl;
if(cnt%2 == 1)
cout<<"unsolvable";
else{
cout<<bfs(s);
}
return 0;
}