HDU 1043 Eight 八数码问题 A*搜索 启发式算法

原题地址:
http://acm.hdu.edu.cn/showproblem.php?pid=1043

题意:给一个3*3的方格,每个格子分别有1,2,3,4,5,6,7,8,x这九个编号,其中x这个格子可以通过与其相邻的上下左右格子交换来移动,问x这个格子要经过怎样的移动可以使方格变成1,2,3,4,5,6,7,8,x的状态,如果怎样也不可到达题目要求状态,输出”
unsolvable“。

思路:某物通过不断改变自己的状态,来达到目标状态的题目可以用搜索?可就算知道用搜索可解,这题仍要考虑一下几点: 1.状态的表示:搜索的时候要防止访问已访问过的状态,所以需要对已访问过的状态进行标记,怎样进行标记比较高效?先想到的是吧这个3*3的矩阵看做1*9的数字,但是单开数组是会爆内存的,用map的话,查找也许不是很高效。可以注意到这个1*9的数字其实是9个数的一个排列,因此我们可以用康拓展开来表示当前数是0-8,9个数组成的排列中的第几个排列。用这个编号作为状态的标记。 2.合法排列的判定:如果一个序列本身就不可以到达目标状态,那么搜索是无用的,且会大大增加程序的运行时间。不难发现由于目标状态是没有逆序对的,单单移动x的话,逆序对的出现或者增加都是成对的,同时题目中也有”
In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing ‘x’ tile, of course). “的提示,自己也不会严格的证明,总之当逆序对的个数为奇数时,是无解的。 3.如何加速搜索:自己第一次接触到A*搜索,百度了一下,这个算法被称作“启发式算法”,该算法通过f值来判断某状态到达目标状态的可能性大小,以此来加速搜索。 其中f=g+h g为初始状态到达当前状态的步数,这个可以通过计数得到。 h为当前状态估计到达目标状态所需的步数,网上很多人在这题中,用每个点到自己目标位置的曼哈顿距离来估值。
每个状态的f值越小,其找到目标状态的所需花费就越小。想必对h值很好理解,其必然是越小越好。可自己在思考题目的时候,思维一直有个误区,认为g值越大越好,自己想当然地以为一个点离开其实状态越远,找到目标状态的可能性越大,可这是错误的。举个直观的例子,在每个状态的h值为0的情况下,f=g,此时A*搜索退化为广搜,对一般的广搜来说,每次从离起始状态最近的未搜索状态进行搜索,此时搜索的依据就是从g值小的开始。自己网上找了些资料,也都是这样说,但是貌似都没有说清这样做的原因,自己也试了试,的确是g越小越好,自己以后想明白了会补上来的。

自己这个代码写的很混乱,大部分参考的是:
http://blog.csdn.net/acm_cxlove/article/details/7745323这里的代码。有需要可以移步这里查看。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#include<stack>
using namespace std;
#define MS(x,y) memset(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define lowbit(x) (x&(-x))
typedef long long LL;
inline void fre1(){freopen("input.txt","r",stdin);/*freopen("output.txt","w",stdout);*/}
inline void fre2(){fclose(stdin);/*fclose(stdout);*/}
const int MAXN=362880+5;
const double EPS=1e-8;
struct Node{
    int maxtrix[3][3];
    int x,y,g,h,hsh;
    bool operator < (const Node& rhs) const{
        return g>rhs.g||(g==rhs.g&&h>rhs.h);
    }
    bool check(){    //?????? 
        if(x>=0&&x<3&&y>=0&&y<3) 
            return true; 
        return false; 
    } 
};
int fact[9]={1,1,2,6,14,120,720,5040,40320};
int get_hash(int ma[][3]){
    int s[9],k=0;
    for(int i=0;i<3;++i)
        for(int j=0;j<3;++j)
            s[k++]=ma[i][j];
//    for(int i=0;i<k;++i) cout<<s[i]<<endl;
    int ret=0,cnt;
    for(int i=0;i<9;++i){
        cnt=0;
        for(int j=0;j<i;++j) if(s[j]>s[i]) ++cnt;
        ret+=cnt*fact[i];
    }
    return ret;
}
int AIM[][3]={1,2,3,4,5,6,7,8,0};
int aim=get_hash(AIM);
const int dir1[]={0,0,1,-1};
const int dir2[]={1,-1,0,0};
char str[30];
int pre[MAXN],vis[MAXN];

bool myjudge(int ma[][3]){
    int s[9],k=0;
    for(int i=0;i<3;++i)
        for(int j=0;j<3;++j)
            s[k++]=ma[i][j];
//    for(int i=0;i<k;++i) cout<<s[i]<<endl;
    int ret=0;
    for(int i=0;i<k;++i) for(int j=i+1;j<k;++j)
        if(s[i]&&s[j]&&s[i]>s[j]) ++ret;
    return !(ret&1);
}
int get_h(int ma[][3]){
    int ret=0;
    for(int i=0;i<3;++i){
        for(int j=0;j<3;++j) if(ma[i][j]){
            ret+=(abs(i-(ma[i][j]-1)/3)+abs(j-(ma[i][j]-1)%3));
        }
        else ret+=4-i-j;
    }
    return ret;
}
stack<char> S;
void print(){
    while(!S.empty()) S.pop();
    int nxt=aim;
    while(~pre[nxt]){
//        cout<<pre[nxt]<<endl;
        if(vis[nxt]==0) S.push('r');
        else if(vis[nxt]==1) S.push('l');
        else if(vis[nxt]==2) S.push('d');
        else if(vis[nxt]==3) S.push('u');
        nxt=pre[nxt];
    }
//    if(S.empty()) cout<<"YES"<<endl;
//    else cout<<"NO"<<endl;
    while(!S.empty()) putchar(S.top()),S.pop();
    putchar(10);
}
void bfs(Node node){
    node.g=0;
    node.h=get_h(node.maxtrix);
//    cout<<node.h<<endl;
    MS(vis,-1);
    MS(pre,-1);
    node.hsh=get_hash(node.maxtrix);
    if(node.hsh==aim){
        putchar(10);
        return;
    }
//    cout<<node.hsh<<endl<<aim<<endl;
    vis[node.hsh]=-2;
    priority_queue<Node> Q;
    while(!Q.empty()) Q.pop();
    Q.push(node);
    Node u,v;
    bool flag=false;
    while(!Q.empty()){
//        cout<<"Y"<<endl;
        u=Q.top();Q.pop();
        for(int i=0;i<4;++i){
            v=u;
            v.x+=dir1[i];v.y+=dir2[i];
            if(v.x<0||v.x>2||v.y<0||v.y>2) continue;
                swap(v.maxtrix[v.x][v.y],v.maxtrix[u.x][u.y]);
                v.hsh=get_hash(v.maxtrix);
    //            cout<<v.hsh<<endl;
                if(vis[v.hsh]==-1){
                    vis[v.hsh]=i;
                    v.g++;
                    pre[v.hsh]=u.hsh;
                    v.h=get_h(v.maxtrix);
                    Q.push(v);
                }
                if(v.hsh==aim){
                    flag=true;
                    break;
                }

        }
        if(flag) break;
    }
    print();
}

int main()
{
    while(gets(str)!=NULL){
//        printf("%s",str);
        int len=strlen(str);
        int i=0,j=0;
        Node node;
        for(int k=0;k<len;++k){
            if((str[k]>='1'&&str[k]<='8')||str[k]=='x'){
                int t=node.maxtrix[i][j]=str[k]-'0';
                if(t<1||t>8){
                    node.maxtrix[i][j]=0;
                    node.x=i,node.y=j;
                }
                ++j;
                if(j==3) ++i,j=0;
            }
        }
//        for(int i=0;i<3;++i){
//            for(int j=0;j<3;++j) printf("%d",node.maxtrix[i][j]);
//            putchar(10);
//        }
        if(myjudge(node.maxtrix)) bfs(node);
        else puts("unsolvable");
    }
    return 0;
}
    原文作者:启发式算法
    原文地址: https://blog.csdn.net/dpppbr/article/details/52081134
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞