hdu 1043 Eight 双向BFS/A*算法

终于被我水过了,哈哈,4640MS,真的是卡时间啊。。,先用这个水水,还没搞清楚什么地方耗太多的时间,别人好像也没用多少时间的啊。之后看了下A*算法,时间上的消耗就减少了很多。

题意:

给定一个序列,由1~8数字和字母x组成,表示的是一个3*3的矩形。每次操作x都能与相邻的数字交换,问如何操作才能使得序列为{1,2,3,4,5,6,7,8,x}。

题解:

1)双向BFS

这题需要特判无法变换到得情况,我们发现每次变换8个数字的逆序数奇偶性都是不变的,所以找出给定的序列的逆序数可以特判不存在的情况。

之后要了解下康托展开(一开始直接map,死的惨惨的),这个是为了哈希用的,就是压缩的排列,使得其变为第几个排列,那样就容易哈希了:对于14032,在排列中排第几个?是1*4!+3*3!+0*2!+1*1!+0*0!,为什么呢,类似于数位dp,第一位比1小的只有1个,后面可以随意排列,既4!种;接着确定第一位为1,第二位比4小的有4个,但1已经用过了,就只有3*3!种情况;接着确定前两位是14...以此类推。

通过康托展开,我们将x=0,那样所有的排列都可以转换成0到9!了,360000左右各值,很容易用数组哈希。

之后是双向BFS,跟单BFS用的一样,从开头和终点一起开始BFS,直到两个BFS相遇,就是最短的路程了。这样可以缩短很长的时间,why?例如一个迷宫,我们假设没有边界,没有墙的空地,单BFS需要4^n时间,那双向BFS就是4^(n/2)*2,缩短了很多。

2)A*算法

A*算法是一种求最短路径的算法,会添加一个评估函数h(n),以减少时间的消耗;BFS是一种特殊的A*算法,h(n)=0。

拓展方式跟BFS类似,不过采用优先队列,以f值小的优先(f=g+h,g为实际到这点的消耗,h为预测到目标点的消耗)。若h<=n(n为实际这点到目标点的消耗),必定存在最优解;若h>n则不一定存在最优解。所以h值越接近n值越好,但不要超过。

具体解释:A*算法-百度百科A*算法入门-博客园





代码:

1.双向BFS

#include <iostream>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
using namespace std;

const int maxn=5e5+10;
int hash[9]={1,1,2,6,24,120,720,5040,40320};
struct node{
    int num;
    char ch;
}pre[maxn];
int vis[maxn];
int vis2[maxn];
struct node2{
    string c;
    int num;
}e;
int dir[4]={-3,3,-1,1};
char a[30],d[10]={"udlr"},d2[10]={"durl"};
string b="123456780";
void show(int x)
{
    if(pre[x].num==-1)return ;
    show(pre[x].num);
    printf("%c",pre[x].ch);
}
int get_hash(string e)//获得hash值,康托展开
{
    int s=0,i,j,k;
    for(i=0;i<9;i++)
    {
        k=0;
        for(j=0;j<i;j++)
            if(e[j]>e[i])k++;
        s+=k*hash[i];
    }
    return s;
}
void bfs()
{
    vis[get_hash(e.c)]=1;
    pre[1].num=-1;
    int num=2,x,i,j,k,p,q;
    node2 f,g;
    f.c=b;
    f.num=8;
    pre[2].num=-1;
    vis2[get_hash(f.c)]=2;
    queue<node2>q1;
    queue<node2>q2;
    q1.push(e);
    q2.push(f);
    while(!q1.empty()&&!q2.empty())
    {
        f=q1.front();
        q1.pop();
        //cout<<f.c<<endl;
        p=get_hash(f.c);
        if(vis2[p])
        {
            show(vis[p]);
            k=vis2[p];
            while(pre[k].num!=-1)
            {
                printf("%c",pre[k].ch);
                k=pre[k].num;
            }
            printf("\n");
            return ;
        }
        for(i=0;i<4;i++)
        {
            if(i==0&&f.num<3)continue;
            if(i==1&&f.num>5)continue;
            if(i==2&&f.num%3==0)continue;
            if(i==3&&f.num%3==2)continue;
            x=f.num+dir[i];
            g=f;
            swap(g.c[f.num],g.c[x]);
            q=get_hash(g.c);
            if(vis[q])continue;
            vis[q]=++num;
            g.num=x;
            pre[num].num=vis[p];
            pre[num].ch=d[i];
            q1.push(g);
        }

        f=q2.front();
        q2.pop();
        p=get_hash(f.c);
        //cout<<f.c<<endl;
        if(vis[p])
        {
            show(vis[p]);
            k=vis2[p];
            while(pre[k].num!=-1)
            {
                printf("%c",pre[k].ch);
                k=pre[k].num;
            }
            printf("\n");
            return ;
        }
        for(i=0;i<4;i++)
        {
            if(i==0&&f.num<3)continue;
            if(i==1&&f.num>5)continue;
            if(i==2&&f.num%3==0)continue;
            if(i==3&&f.num%3==2)continue;
            x=f.num+dir[i];
            g=f;
            swap(g.c[f.num],g.c[x]);
            q=get_hash(g.c);
            if(vis2[q])continue;
            vis2[q]=++num;
            g.num=x;
            pre[num].num=vis2[p];
            pre[num].ch=d2[i];
            q2.push(g);
        }
    }
    printf("unsolvable\n");
}
int main()
{
    //freopen("C:\\Documents and Settings\\Administrator\\桌面\\in.txt","r",stdin);
    //freopen("C:\\Documents and Settings\\Administrator\\桌面\\out.txt","w",stdout);
    while(gets(a))
    {
        int i,j,k=0,n;
        n=strlen(a);
        e.c="";
        for(i=0,j=0;i<n;i++)
            if(a[i]!=' ')
            {
                if(a[i]=='x'){e.num=j;e.c+='0';}
                else e.c+=a[i];
                j++;
            }
        for(i=0;i<9;i++)
        {
            //printf("%c\n",e.c[i]);
            if(e.c[i]=='0')continue;
            for(j=0;j<i;j++)
            {
                if(e.c[j]=='0')continue;
                if(e.c[j]>e.c[i])k++;
            }
        }
        memset(vis2,0,sizeof(vis2));
        memset(vis,0,sizeof(vis));
        if(k&1)printf("unsolvable\n");
        else bfs();
        //printf("%d %c\n",pre[3].num,pre[3].ch);
    }
    return 0;
}


2.A*算法:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <vector>
using namespace std;

const int maxn=4e5+10;
int ha[9]={1,1,2,6,24,120,720,5040,40320};
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
char d[10]="udlr";
int vis[maxn];
struct node{
    int f[3][3];
    int x,y;
    int g,h;
    int hash_num;
    bool operator < (const node a)const{
        //return h==a.h?g>a.g:h>a.h; //281MS
        return h+g>a.h+a.g; //859ms
    }
};
struct path{
    int pre;
    char ch;
}p[maxn];
int get_hash(node e)//康托展开,压缩空间。
{
    int a[9],i,j,k=0,ans=0;
    for(i=0;i<3;i++)
    {
        for(j=0;j<3;j++)
            a[k++]=e.f[i][j];
    }
    for(i=0;i<9;i++)
    {
        k=0;
        for(j=0;j<i;j++)
            if(a[j]>a[i])k++;
        ans+=ha[i]*k;
    }
    return ans;
}
//评估函数,获得评估值
//计算1~8的数字回到原点需要的步数作为评估值,必定小于实际操作数
int get_h(node e)
{
    int i,j,ans=0;
    for(i=0;i<3;i++)
    {
        for(j=0;j<3;j++)
        {
            if(e.f[i][j])
            ans+=abs(i-(e.f[i][j]-1)/3)+abs(j-(e.f[i][j]-1)%3);
        }
    }
    return ans;
}
void print(int x)
{
    if(p[x].pre==-1)return;
    print(p[x].pre);
    printf("%c",p[x].ch);
}
void A_star(node e)
{
    memset(vis,0,sizeof(vis));
    int i,j,k,end_ans,xx,yy;
    node a,b;
    for(i=0;i<9;i++)
        a.f[i/3][i%3]=(i+1)%9;
    end_ans=get_hash(a);
    e.hash_num=get_hash(e);
    e.g=0;e.h=get_h(e);
    vis[e.hash_num]=1;
    p[e.hash_num].pre=-1;

    if(e.hash_num==end_ans){printf("\n");return;}

    priority_queue<node>q;
    q.push(e);
    while(!q.empty())
    {
        e=q.top();
        q.pop();
        //printf("%d\n",e.hash_num);
        for(i=0;i<4;i++)
        {
            xx=e.x+dir[i][0];
            yy=e.y+dir[i][1];
            if(xx<0||yy<0||xx>=3||yy>=3)continue;
            a=e;
            swap(a.f[e.x][e.y],a.f[xx][yy]);
            k=get_hash(a);
            if(vis[k])continue;
            vis[k]=1;
            a.hash_num=k;
            a.x=xx;
            a.y=yy;
            a.g++;
            a.h=get_h(a);
            p[k].pre=e.hash_num;
            p[k].ch=d[i];
            if(k==end_ans)
            {
                print(k);
                printf("\n");
                return ;
            }
            q.push(a);
        }
    }
}
int main()
{
   /* node e;
    e.f[0][0]=2;e.f[0][1]=3;e.f[0][2]=4;
    e.f[1][0]=1;e.f[1][1]=5;e.f[1][2]=0;
    e.f[2][0]=7;e.f[2][1]=6;e.f[2][2]=8;
    printf("*%d\n",get_hash(e));*/
    char a[30];
    while(gets(a))
    {
        int i,j,k,n;
        node e;
        n=strlen(a);
        for(i=0,j=0;i<n;i++)
        {
            if(a[i]==' ')continue;
            if(a[i]=='x'){e.f[j/3][j%3]=0;e.x=j/3;e.y=j%3;}
            else e.f[j/3][j%3]=a[i]-'0';
            j++;
        }
        //判断逆序数
        for(i=0,k=0;i<9;i++)
        {
            if(e.f[i/3][i%3]==0)continue;
            for(j=0;j<i;j++)
            {
                if(e.f[j/3][j%3]==0)continue;
                if(e.f[j/3][j%3]>e.f[i/3][i%3])k++;
            }
        }
        if(k&1)printf("unsolvable\n");
        else A_star(e);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值