[luogu]P1379 八数码难题[广度优先搜索]

八数码难题

——!x^n+y^n=z^n

  我在此只说明此题的一种用BFS的方法,因为本人也是初学,勉勉强强写了一个单向的BFS,据说最快的是IDA*(然而蒟蒻我不会…)

  各位如果想用IDA*的可以看看这位大佬的这篇文章:

http://www.cnblogs.com/ZYBGMZL/p/6852733.html

  接下来是我的方法,用luogu的跑了最慢是200ms,感觉还行把。

题目描述

在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

输入输出格式

输入格式:

输入初试状态,一行九个数字,空格用0表示

输出格式:

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

输入输出样例

输入样例#1

283104765

输出样例#1

4

  因为这是要最优解且保证数据有解,于是就想到了BFS。

  然而这个过程是有许多障碍的,要怎样检验自己的状态是否为解,还有判重的操作,如果你没有判重,TLE即在眼前…

所以我们可以想到压缩状态!当然如果用二进制难免有点力不从心,那我们干脆存成整数不就行了?但是可能你会发现,会有前导零的情况,怎么办?

这时候其实可以在状态前加一个1,在int型中还是过得去的。

  那判重怎么搞?注意到这只有9!种状态。

  想到什么?康托尔展开!对于0~8的全排列,

  012345678 的字典序是1,如果让你手动操作我想没什么问题,那怎么让计算机做这件事?

  对于 (a(n-1) a(n-2)L a(0))的字典序计算方法为:

  Σ(ci*i!)ci为当前未出现的比ai小的数的个数。

  既然这样,我们就能把状态一一存下来了,交换的话很简单,读者手动操作即可发现规律。

  简单说明之后附上代码参考一下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 400000
//阶乘?我当然打表啦。 
int fac[10]={1,1,2,6,24,120,720,5040,40320,362880};
//状态 
struct sjs{
    int num;
    int pos;
}state[maxn];
//队列 
struct _757{
    int time;
    int now;
    int fd;
}qu[maxn];
int cto[9];
//判重 
bool rep[maxn];
int head,tail;
//特殊嗜好??? 
namespace lys{
    //快速幂 
    int fpow(int p){
        int res=1,base=10;
        while(p>0){
            if(p&1) res*=base;
            base*=base;
            p>>=1;
        }
        return res;
    }
    //计算cantor 
    int cantor(int num){
        int i=0,pos,res=0;
        int x=num;
        memset(cto,0,sizeof cto);
        while(num>0){
            cto[i]=num%10;
            if(cto[i]==0) pos=i;
            num/=10;
            i++;
        }
        int j,cal;
        for(i=8;i>=0;i--){
            cal=0;
            for(j=8;j>i;j--){
                if(cto[j]<cto[i]) cal++;
            }
            res+=(cto[i]-cal)*fac[i];
        }
        state[res+1].num=x;
        state[res+1].pos=pos;
        return res+1;
    }
    //判断是否能移动 
    bool chk(int pos,int i){
        switch(i){
            case 1:if(pos<6) return true ; return false ;
            case 2:if(pos>2) return true ; return false ;
            case 3:if(pos==8||pos==5||pos==2) return false ; return true ;
            case 4:if(pos==0||pos==3||pos==6) return false ; return true ;
        }
    }
    int bfs(){
        int t=0,i,st,ch,fp,x,num;
        do{
            st=qu[head].now;
            fp=fpow(state[st].pos);
            num=state[st].num;
            for(i=1;i<=4;i++){
                if(chk(state[st].pos,i)){
                    switch(i){
                        case 1:
                            x=(num/(fp*1000))%10;
                            ch=num-x*fp*1000+x*fp;
                            x=cantor(ch);
                            qu[++tail].now=x;
                            qu[tail].time=(qu[head].time+1);
                            qu[tail].fd=head;
                            //目标态,觉得不这样写也行,直接用num比较 
                            if(x==46686){
                                return qu[tail].time;
                            }
                            if(rep[x]) tail--;
                            else rep[x]=true ;
                            break ;
                        case 2:
                            x=(num/(fp/1000))%10;
                            ch=num-x*fp/1000+x*fp;
                            x=cantor(ch);
                            qu[++tail].now=x;
                            qu[tail].time=(qu[head].time+1);
                            qu[tail].fd=head;
                            if(x==46686){
                                return qu[tail].time;
                            }
                            if(rep[x]) tail--;
                            else rep[x]=true ;
                            break ;
                        case 3:
                            x=(num/(fp*10))%10;
                            ch=num+x*fp-x*fp*10;
                            x=cantor(ch);
                            qu[++tail].now=x;
                            qu[tail].time=(qu[head].time+1);
                            qu[tail].fd=head;
                            if(x==46686){
                                return qu[tail].time;
                            }
                            if(rep[x]) tail--;
                            else rep[x]=true ;
                            break ;
                        case 4:
                            x=(num/(fp/10))%10;
                            ch=num+x*fp-x*fp/10;
                            x=cantor(ch);
                            qu[++tail].now=x;
                            qu[tail].time=(qu[head].time+1);
                            qu[tail].fd=head;
                            if(x==46686){
                                return qu[tail].time;
                            }
                            if(rep[x]) tail--;
                            else rep[x]=true ;
                            break ;
                    }
                }        
            }
            head++;
        }while(head<=tail);
    }
    int main(){
        int i,j;
        char c;
        int st=0;
        for(i=1;i<=3;i++){
            for(j=1;j<=3;j++){
                c=getchar();
                while(c<'0'||c>'9') c=getchar();
                st=st*10+c-'0';
            }
        }
        //初始状态 
        int r=cantor(1000000000+st);
        qu[++head].now=r;
        qu[head].time=0;
        rep[r]=true ;
        tail=1;
        printf("%d
",bfs());
        return 0;
    }
}
int main(){
    lys::main();
    return 0;
}
原文地址:https://www.cnblogs.com/_inx/p/7228928.html