A*算法——解经典八数码问题

题目连接

问题引入:在进行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;
}



  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值