poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)

Posted acboyty

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)相关的知识,希望对你有一定的参考价值。

题目来源:

http://poj.org/problem?id=1077

 

题目大意:

给你一个由1到8和x组成的3*3矩阵,x每次可以上下左右四个方向交换。求一条路径,得到12345678x这样的矩阵。若没有路径,则输出unsolvable。

 

经典的八数码问题。

这题我用A*算法做的。推荐一篇博客,从大体上介绍了一下启发式算法的代表A*算法:

https://www.cnblogs.com/zhoug2020/p/3468167.html

 

说说这道题的几个注意点:

首先就是判重的问题,搜索的状态是九个数(含x),开个九重数组也不是不可以,但用cantor展开hash一下还是方便的。所谓cantor展开,就是对1..n的所有排列,唯一对应一个1..n!的值。代码中的cantor展开,可以当做模板用。

其次就是奇偶剪枝了。对于1..n的排列来说,每次对换两个元素,排列逆序数的奇偶性一定改变。而容易发现,和x交换,1..8的排列逆序数奇偶性是不变的。故可以实现判断是否是unsolvable。

最后就是Astar算法本身了:

首先需要一个优先队列。由于对优先级的需要,我们需要在结构体中对<运算符进行重载(详见代码)。

然后结构体中存储路径最好还是用string吧,毕竟vector还是没它方便。

好像就是这么多。搜索这种东西,复杂度比较难把握,所以能优化的地方尽量优化一下吧。比如搜索方向,感觉还是先搜索down和right比较好,虽然可能效果不太明显。

 

这道题还要继续做。hdu1043还没A掉,双向BFS,还有什么打表方法还没有尝试写呢。加上!!!,表示要记得做!

 

技术分享图片
#include<iostream>
#include<cstdio>
#include<string>
#include<queue>
#include<cstring>

using namespace std;

char buf[30];
int puz[9];

int fac[]={1,1,2,6,24,120,720,5040,40320,362880};
int cantor(int s[])
{
    int sum=0;
    for(int i=0;i<9;i++)
    {
        int num=0;
        for(int j=i+1;j<9;j++)
          if(s[j]<s[i])
            num++;
        sum+=(num*fac[9-i-1]);
    }
    return sum+1;
}

struct tnode
{
    int puz[9];
    string path;
    int loc;//0的位置
    int status;//cantor展开值
    int n;//搜索深度
    int f;//估值函数
    int getloc()
    {
        for(int i=0;i<9;i++)
            if(puz[i]==0)
                return i;
    }
    bool operator<(const tnode& y) const
    {
        return f>y.f;
    }
};
int vis[363000];
int dis[9]={4,3,2,3,2,1,2,1,0};
int a[4]={1,3,-1,-3};
char b[5]="rdlu";

int aim=46234;

int main()
{
    while(scanf("%[^
]",buf)!=EOF)
    {
        getchar();

        for(int i=0,cnt=0;buf[i]!=;i++)
        {
            if(buf[i]==x)
                puz[cnt++]=0;//用0存储x
            else if(buf[i]>=1&&buf[i]<=8)
                puz[cnt++]=buf[i]-0;
        }
        //for(int i=0;i<9;i++) printf("%d ",puz[i]); printf("
");
        //printf("%d
",cantor(puz));

        int cnt=0;
        for(int i=0;i<9;i++)
            if(puz[i]!=0)
            {
                for(int j=i+1;j<9;j++)
                    if(puz[j]!=0&&puz[j]<puz[i])
                        cnt++;
            }
        if(cnt%2==1)
        {
            printf("unsolvable
");
            continue;
        }

        priority_queue<tnode> q;
        memset(vis,0,sizeof(vis));
        tnode node1;
        memcpy(node1.puz,puz,sizeof(puz));
        node1.path="";
        node1.loc=node1.getloc();
        node1.status=cantor(node1.puz);
        node1.n=0;
        node1.f=node1.n+dis[node1.loc];
        if(node1.status==aim)//入队之前先判断,可以加快速度
        {
            cout<<node1.path<<endl;
            continue;
        }
        q.push(node1);
        vis[node1.status]=1;
        bool flag=false;
        while(!q.empty())
        {
            tnode node2=q.top();q.pop();
            bool fflag=false;
            for(int i=0;i<4;i++)
            {
                tnode node3=node2;
                //printf("%d ",node3.loc);
                if(a[i]==1&&node3.loc%3<=1)
                {
                    node3.puz[node3.loc]=node3.puz[node3.loc+a[i]];
                    node3.puz[node3.loc+a[i]]=0;
                    node3.status=cantor(node3.puz);
                }
                else if(a[i]==-1&&node3.loc%3>=1)
                {
                    node3.puz[node3.loc]=node3.puz[node3.loc+a[i]];
                    node3.puz[node3.loc+a[i]]=0;
                    node3.status=cantor(node3.puz);
                }
                else if(a[i]==3&&node3.loc<=5)
                {
                    node3.puz[node3.loc]=node3.puz[node3.loc+a[i]];
                    node3.puz[node3.loc+a[i]]=0;
                    node3.status=cantor(node3.puz);
                }
                else if(a[i]==-3&&node3.loc>=3)
                {
                    node3.puz[node3.loc]=node3.puz[node3.loc+a[i]];
                    node3.puz[node3.loc+a[i]]=0;
                    node3.status=cantor(node3.puz);
                }
                //printf("%d ",node3.status);
                if(!vis[node3.status])
                {
                    node3.path+=b[i];
                    node3.loc=node3.getloc();
                    node3.n++;
                    node3.f=node3.n+dis[node3.loc];
                    q.push(node3);
                    vis[node3.status]=1;
                }
                if(node3.status==aim)
                {
                    node1=node3;
                    fflag=true;
                    break;
                }
            }
            if(fflag)
            {
                flag=true;
                break;
            }
        }

        if(flag)
        {
            cout<<node1.path<<endl;
        }
        else
        {
            printf("unsolvable
");
        }
    }
    return 0;
}
View Code

 

以上是关于poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)的主要内容,如果未能解决你的问题,请参考以下文章

《算法竞赛进阶指南》0x27A* 八数码问题 POJ1077

POJ1077 八数码问题

八数码的A*与IDA*算法-搜索进阶练习1

Hdu 1043 Eight (八数码问题)

POJ 1077 Eight(bfs+康托展开)

POJ 1077 Eight(康托展开+BFS)