POJ 3221:Diamond Puzzle(六数码 反向bfs)


题目链接:点击打开链接

题目大意:

类似于八数码问题,但是问题简化到了六数码。大体上是差不多的。

解题思路:

在学校的一场团队赛碰到的题目,因为之前做过八数码,本来想做的,但是发现自己不会康托展开的hash打表,因为之前的hash打表是照搬着网上抄的,根本没什么印象,而且当时不知道六数码问题到底什么情况下得不到解,所以就放弃了,觉得很可惜。

解题思路应该和八数码一样,利用bfs()搜索+hash存状态得到最短步数即可,之前的八数码问题因为情况复杂一点要用上A*算法,否则就会t掉,然后赛后我搞完了hash打表之后开始做这道六数码问题,写的时候感觉,六数码,应该不用A*吧,直接写,然后tle,难道还要我用A*?觉得不信,因为自己找不到的情况是暴力的方法,觉得可能是自己的这部分有问题遂去网上找题解看一下他们怎么处理的,然后发现他们没处理,我的天,但是看到了反向bfs()几个字,纠结了好长时间反向bfs和正向bfs到底有啥区别呢,突然惊奇的发现反向bfs可以打表,打表我的天哪,bfs竟然打表,然后稍微打了一下表,还是t,我去,什么情况。后来看了别人代码发现了问题,我自己的表只是每次算每个数据的时候顺带打个表,然后题解是直接在输入数据之外打的表,惊了,六数码的情况是有多少,头一次见bfs还能这么打表的。真的惊了,还是自己做题太少。

顺带吐槽一下这题每个点能走的方向不一样真的是恶心到极致,再顺带吐槽一下自己找的几篇博客代码几乎都一模一样,有的甚至连代码之外的解析部分都相同,不得不承认,题解的代码比我短好多,一会儿去学习一下。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define next ne
using namespace std;
typedef long long ll;
int INF=1e9+7;
int n,m,res;
char a2[10];
int a1[10];
int dir[10][4];
vector <int> v[10];
int ha[10]={1,1,2,6,24,120,720,5040,40320}; //康托展开需要用到的表
int us[1000000];
int pr[1000000];    //打表函数
int dx[8]={-1,-1,0,1,1,1,0,-1}; //方向数组,注意每个点表示的方向之后会用到
int dy[8]={0,1,1,1,0,-1,-1,-1};
struct node //每种状态
{
    int f[3][3];
    int x,y;
    int step;
    int haha;
}start,en;
int get_hash(node e)    //得到每种状态对应的hash值
{
    int a[9],k=0,ans=0;
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<3;j++)
            a[k++]=e.f[i][j];
    }
    for(int i=0;i<9;i++)
    {
        k=0;
        for(int j=0;j<i;j++)
            if(a[j]<a[i]) k++;
            ans=ans+ha[i]*k;
    }
    return ans;
}
void bfs()  //反向bfs
{
    queue<node> que;
    en.step=0;
    que.push(en);
    us[en.haha]=1;
    while(!que.empty())
    {
        node k=que.front();
        que.pop();
        pr[k.haha]=k.step;  //对找到的每一种状态打表
        node k1;
        for(int i=0;i<(int)v[en.f[k.x][k.y]].size();i++)    //利用vector得到每个点所能走的方向
        {
            int xx=dx[v[en.f[k.x][k.y]][i]]+k.x;
            int yy=dy[v[en.f[k.x][k.y]][i]]+k.y;
            if(xx>=0&&xx<3&&yy>=0&&yy<3)    //正常的bfs过程,判断是否符合要求
            {
                k1=k;
                swap(k1.f[xx][yy],k1.f[k.x][k.y]);
                k1.haha=get_hash(k1);
                if(us[k1.haha]==0)
                {
                    k1.step=k.step+1;
                    k1.x=xx;
                    k1.y=yy;
                    us[k1.haha]=1;
                    que.push(k1);
                }
            }
        }
    }
}
int main()
{
    int QAQ;
    scanf("%d",&QAQ);   //初始化-1,因为找不到输出-1
    memset(pr,-1,sizeof(pr));   //下面就是及其恶心的一步,将每个点能走的方向保存一下
    v[0].push_back(2);v[0].push_back(4);v[0].push_back(6);
    v[1].push_back(3);v[1].push_back(5);
    v[2].push_back(4);v[2].push_back(6);v[2].push_back(7);
    v[3].push_back(0);v[3].push_back(6);
    v[4].push_back(0);v[4].push_back(2);v[4].push_back(6);
    v[5].push_back(0);v[5].push_back(2);
    v[6].push_back(1);v[6].push_back(2);v[6].push_back(4);
    en.f[1][1]=0;
    en.f[0][1]=1;
    en.f[1][2]=2;
    en.f[2][2]=3;
    en.f[2][1]=4;
    en.f[2][0]=5;
    en.f[1][0]=6;
    en.x=1;
    en.y=1;
    en.haha=get_hash(en);
    bfs();      //暴力打表
    while(QAQ--)
    {
        scanf(" %s",a2);
        for(int i=0;i<7;i++)    //处理起始状态
            a1[i]=a2[i]-'0';
        start.f[0][0]=-1;
        start.f[0][2]=-1;
        start.f[1][1]=a1[0];
        start.f[0][1]=a1[1];
        start.f[1][2]=a1[2];
        start.f[2][2]=a1[3];
        start.f[2][1]=a1[4];
        start.f[2][0]=a1[5];
        start.f[1][0]=a1[6];
        for(int i=0;i<3;i++)
        {
            for(int j=0;j<3;j++)
            {
                if(start.f[i][j]==0)
                {
                    start.x=i;
                    start.y=j;
                }
            }
        }
        start.f[0][0]=0;
        start.f[0][2]=0;
        start.haha=get_hash(start);
        printf("%d\n",pr[start.haha]);  //因为打过表,直接输出
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值