UVA589 Pushing Boxes

UVA589 Pushing Boxes

题目地址

题意 : 推箱子游戏

  • 这里提供一种图论解法 。

  • 设计状态为四元组 ( P x , P y , B x , B y ) (Px,Py,Bx,By) (Px,Py,Bx,By) 分别代表人的坐标和箱子的坐标 。

  • 设计路程为二元组 D i s ( P u s h , W a l k ) Dis(Push,Walk) Dis(Push,Walk) 分别代表推箱子的次数和走路的次数。

  • 路程 A A A大于路程 B B B ,当 A . P u s h > B . P u s h 或 ( A . P u s h = = B . P u s h 且 A . W a l k > B . W a l k ) A.Push >B.Push或(A.Push==B.Push且A.Walk>B.Walk) A.Push>B.Push(A.Push==B.PushA.Walk>B.Walk)

    bool Cmp (const Dis &A , const Dis &B) {
        if (A.Push != B.Push ) return A.Push > B.Push ;
        else return A.Walk > B.Walk;
    }

  • 两个路程叠加,需要把两个属性分别叠加:

    Dis Plus (Dis &A , Dis &B) {
        Dis C ;
        C.Push = A.Push + B.Push ;
        C.Walk = A.Walk + B.Walk ;
        return C ;
    }
  • 然后用死了的 S P F A SPFA SPFA 就可以解决图论问题了

CODE

#include 
#include 
#define re register
#define Clean(X,K) memset(X,K,sizeof(X))
#define Jud(X,Y) (X<1||X>N||Y<1||Y>M||A[X][Y])
#define GC getchar()
int Qread () {
    int X = 0 ;
    char C = GC ;
    while (C > '9' || C < '0') C = GC ;
    while (C >='0' && C <='9') {
        X = X * 10 + C - '0' ;
        C = GC ;
    }
    return X ;
}
const int Maxn = 25 , Maxq = Maxn * Maxn , INF = 20021020 * 5 , Dx[]= {-1,1,0,0} , Dy[]= {0,0,-1,1};
const char C[]= {'N','S','W','E'} , c[]= {'n','s','w','e'} ;
char  Ans[Maxn * Maxn] = "";
int   Vis[Maxn][Maxn][Maxn][Maxn] ,N , M ,A[Maxn][Maxn] , Ex , Ey , Px[Maxn * Maxn * Maxn * Maxn] , Py[Maxn * Maxn * Maxn * Maxn] , Bx[Maxn * Maxn * Maxn * Maxn] , By[Maxn * Maxn * Maxn * Maxn];
char Nxt[Maxn][Maxn][Maxn][Maxn];
struct Node {
    int X1 ,Y1 , X2 , Y2 ;
};
Node FT[Maxn][Maxn][Maxn][Maxn] , E;
Node Findf(Node X) {
    if (FT[X.X1][X.Y1][X.X2][X.Y2].X2!=0)FT[X.X1][X.Y1][X.X2][X.Y2] = Findf(FT[X.X1][X.Y1][X.X2][X.Y2]) ;
    int Ln = strlen (Ans) ;
    Ans[Ln] = Nxt[X.X1][X.Y1][X.X2][X.Y2];
    Ans[Ln + 1] = '\0' ;
    return FT[X.X1][X.Y1][X.X2][X.Y2] ;
}
struct Dis {
    int Push , Walk ;
};
Dis Plus (Dis &A , Dis &B) {
    Dis C ;
    C.Push = A.Push + B.Push ;
    C.Walk = A.Walk + B.Walk ;
    return C ;
}
bool Cmp (const Dis &A , const Dis &B) {
    if (A.Push != B.Push ) return A.Push > B.Push ;
    else return A.Walk > B.Walk;
}
/*
头文件和准备工作
1. SPFA用到的队列分别是 Px[Maxn * Maxn * Maxn * Maxn] , Py[Maxn * Maxn * Maxn * Maxn] , Bx[Maxn * Maxn * Maxn * Maxn] , By[Maxn * Maxn * Maxn * Maxn];
各自代表了人的坐标,箱子的坐标。
2.定义距离为二元组(推动的次数,走的步数)
3.定义比较和叠加函数Plus和Cmp ;
4.Nxt数组记录了每一步所走的方向,用于递归输出路径。 
*/ 
Dis Mdis[Maxn][Maxn][Maxn][Maxn] ;
Dis One , Zero , _INF ;
Dis SPFA() {
    int H = 0 , T = 1 ;
    Dis Ans = _INF ;
    Clean( Vis , 0) , Clean(Mdis , 0x3f ) ;
    Clean(FT, 0) ;
    Mdis[Px[1]][Py[1]][Bx[1]][By[1]] = Zero ;
    Vis[Px[1]][Py[1]][Bx[1]][By[1]] = 1 ;
    while (H < T) {
        ++ H ;
        Vis[Px[H]][Py[H]][Bx[H]][By[H]] = 0 ;
        Node XX;
        XX.X1 = Px[H] , XX.Y1 = Py[H] , XX.X2 = Bx[H] , XX.Y2 = By[H] ;
        if (Bx[H] == Ex && By[H] == Ey) {
            if (Cmp (Ans , Mdis[Px[H]][Py[H]][Bx[H]][By[H]] ) ) {
                Ans = Mdis[Px[H]][Py[H]][Bx[H]][By[H]] ;
                E.X1 = Px[H] , E.Y1 = Py[H] , E.X2 = Bx[H] , E.Y2 = By[H] ;
            }
        }

        for (re int i = 0 ; i < 4 ; ++ i) {
            int Kx = Dx[i] + Px[H] , Ky = Dy[i] + Py[H];
            if (Jud (Kx , Ky)) continue ;
            if (Kx == Bx[H] && Ky == By[H]) {
                int Tx = Dx[i] + Bx[H] , Ty = Dy[i] + By[H] ;
                if (Jud (Tx , Ty)) continue ;
                if (Cmp (Mdis[Kx][Ky][Tx][Ty] , Plus (Mdis[Px[H]][Py[H]][Bx[H]][By[H]] , One))) {
                    Mdis[Kx][Ky][Tx][Ty] = Plus (Mdis[Px[H]][Py[H]][Bx[H]][By[H]] , One) ;
                    FT[Kx][Ky][Tx][Ty] = XX ;
                    Nxt[Kx][Ky][Tx][Ty] = C[i] ;
                    if (Vis[Kx][Ky][Tx][Ty]) continue ;
                    ++ T ;
                    Vis[Kx][Ky][Tx][Ty] = 1 ;
                    Px[T] = Kx , Py[T] = Ky , Bx[T] = Tx , By[T] = Ty;
                }
                continue ;
            }
            if (Cmp (Mdis[Kx][Ky][Bx[H]][By[H]]  ,Mdis[Px[H]][Py[H]][Bx[H]][By[H]] )) {
                Mdis[Kx][Ky][Bx[H]][By[H]] = Mdis[Px[H]][Py[H]][Bx[H]][By[H]] ;
                FT[Kx][Ky][Bx[H]][By[H]] = XX ;
                Nxt[Kx][Ky][Bx[H]][By[H]] = c[i] ;
                if (Vis[Kx][Ky][Bx[H]][By[H]]) continue ;
                ++ T ;
                Vis[Kx][Ky][Bx[H]][By[H]] = 1 ;
                Px[T] = Kx , Py[T] = Ky , Bx[T] = Bx[H] , By[T] = By[H] ;
            }
        }
    }
    return Ans;
}
/*
SPFA求最短路
实际上还是SPFA的模板,修改了路线长短的判断。 
*/
int main () {
    One.Push = 1 ;
    One.Walk = 0 ;
    Zero.Push = Zero.Walk = 0;
    _INF.Push = _INF.Walk = INF;
    
/*
这里定义了One和Zero,分别是0步和推1步,可以直接用在SPFA里面。 
*/
    //freopen ("UVA589.in" , "r" , stdin) ;
    N = Qread () , M = Qread() ;
    int T = 0 ;
    while (N + M) {
        ++T ;
        Clean(Ans,  0) ;
        Ans[0] = '\0' ;
        Clean(A , 0)  ;
        Clean(Nxt , 0) ;
        for (re int i = 1 ; i <= N; ++ i)
            for (re int j = 1 ; j <= M; ++ j) {
                char C = GC ;
                while (C != '.' && C != '#' && C !='S' && C != 'T' && C != 'B') C = GC ;
                A[i][j] = (C == '#' ? 1 : 0) ;
                if (C == 'S') Px[1] = i , Py[1] = j ;
                if (C == 'B') Bx[1] = i , By[1] = j ;
                if (C == 'T') Ex= i , Ey = j ;
            }
//读入数据
        Dis MinL = SPFA() ;//SPFA 计算最短路 
        Findf(E) ;//递归查找路径 
        printf ("Maze #%d\n" , T) ;
        if (MinL.Push  == INF) printf ("Impossible.\n");
        else
        printf ("%s\n" , Ans) ;
        putchar(10) ;
        N = Qread() , M = Qread() ;
    }
    fclose (stdin) ;
    fclose (stdout);
    return 0;
}

Thanks!

你可能感兴趣的:(图论,乱搞,搜索)