HDU 1043 [Eight] 八数码

关键思想:关于这个问题,有大牛给出了八境界,我先介绍一种比较容易理解的。BFS+康托展开。

康托展开其实就是把一个排列和一个整数对应起来,比如我们把空格当作9,那八数码的每个格局就是9的全排列中的某一个排列,经过康托展开可以把这个排列映射成一个整数,123456789是第一个排列,987654321是最后一个排列.

考虑到题目的输出是多组的,那我们其实可以把所有状态都从123456789开始BFS遍历出来,组成一个图,总共不过9!,40w+状态,BFS可以搞定。

那BFS的结点该怎么构造呢?我本来想着只用hash值,前驱,和空白格的位置来表示,不愿保存格局,太小气了。后来想象其实每次状态转移都要用到这个格局的,解码也需要时间,没有必要,所以一个结点要保存的信息应该有这样几个:当前格局,空白格位置(省的每次都要遍历一下找空白格)。关键思想就是这样,接下来就是耐心地实现了。

我们要弄清楚,BFS获得的图是一棵树,这棵树每个结点对应一个可达的格局(一个额外规律是他们逆序数都是偶数),并且每个格局都是不同的,所有非目标格局出现的位置在离目标格局最近的位置。

代码如下:

#include <iostream>
#include <cstdio> 
#include <algorithm>
#include <vector>
#include <queue>
#include <string.h>
using namespace std;

const int MAXS=366666;

int fac[11]={1,1,2,6,24,120,720,5040,40320,362880,362880};//康托展开用,阶乘数组 
int dir[4][2]={0,1,0,-1,1,0,-1,0}; //方向控制 
bool vis[MAXS];
int from[MAXS][2];//记录路径 :0为前驱结点,1为方向 

struct node{
    int space;
    char a[9];//格局 
    node(){} 
}; 

int EnKT(char* s){//康托展开 
    int ans=1,cnt;
    for(int i=0;i<9;i++){
        cnt=0;
        for(int j=i+1;j<9;j++){//找到后面比较小的数,他们在i处的排列已经历数过
            if(s[j]<s[i])cnt++;
        }
        ans+=cnt*fac[8-i];
    }
    return ans;
}

void BFS(){//BFS预处理 
    //变量声明 
    node nw,nt;
    queue<node>q;
    int Cont;
    //目标状态入队 
    for(int i=0;i<9;i++)nw.a[i]='0'+i+1;
    nw.space=8;
    Cont=EnKT(nw.a);
    vis[Cont]=true;
    from[Cont][0]=Cont;//目标格局自反 
    q.push(nw);

    while(!q.empty()){
        nw=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            int x=nw.space/3+dir[i][0],y=nw.space%3+dir[i][1];
            if(x>=0&&x<=2&&y>=0&&y<=2){
                //生成next结点 
                nt.space=3*x+y;
                memcpy(nt.a,nw.a,9*sizeof(char));
                swap(nt.a[nt.space],nt.a[nw.space]);
                
                Cont=EnKT(nt.a);
                if(!vis[Cont]){
                    from[Cont][0]=EnKT(nw.a),from[Cont][1]=i;//记录路径 
                    q.push(nt);
                    vis[Cont]=true;
                }
            }
        }
    }
    return ; 
}

int main(){
    char tmp[30],start[9]; 
    BFS(); 
    while(gets(tmp)){
        for(int i=0,cnt=0;i<strlen(tmp);i++){
            if(tmp[i]<='9'&&tmp[i]>='1')
                start[cnt++]=tmp[i];
            else if(tmp[i]=='x')start[cnt++]='9';
        }
        int Cont=EnKT(start);
        if(vis[Cont]){//如果状态图中有它 
            int i=Cont;
            while(i!=from[i][0]){//输出时要反着来哦, 
                if(from[i][1]==0)printf("l");
                else if(from[i][1]==1)printf("r");
                else if(from[i][1]==2)printf("u");
                else if(from[i][1]==3)printf("d");
                i=from[i][0];
            }
            printf("\n"); 
        }else{
            printf("unsolvable\n");
        }
    }
    return 0;
} 

 

    原文作者:哇咔咔咔
    原文地址: https://www.cnblogs.com/G-M-WuJieMatrix/p/7491846.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞