1. 程式人生 > >hihocoder 1828 Saving Tang Monk II (DP+BFS)

hihocoder 1828 Saving Tang Monk II (DP+BFS)

ger inpu 分析 ace ren inf 下一個 復雜度 推出

題目鏈接

Problem Description 《Journey to the West》(also 《Monkey》) is one of the Four Great Classical Novels of Chinese literature. It was written by Wu Cheng‘en during the Ming Dynasty. In this novel, Monkey King Sun Wukong, pig Zhu Bajie and Sha Wujing, escorted Tang Monk to India to get sacred Buddhism texts.

During the journey, Tang Monk was often captured by demons. Most of demons wanted to eat Tang Monk to achieve immortality, but some female demons just wanted to marry him because he was handsome. So, fighting demons and saving Monk Tang is the major job for Sun Wukong to do.

Once, Tang Monk was captured by the demon White Bones. White Bones lived in a palace and she cuffed Tang Monk in a room. Sun Wukong managed to get into the palace, and he wanted to reach Tang Monk and rescue him.

The palace can be described as a matrix of characters. Different characters stand for different rooms as below:

‘S‘ : The original position of Sun Wukong

‘T‘ : The location of Tang Monk

‘.‘ : An empty room

‘#‘ : A deadly gas room.

‘B‘ : A room with unlimited number of oxygen bottles. Every time Sun Wukong entered a ‘B‘ room from other rooms, he would get an oxygen bottle. But staying there would not get Sun Wukong more oxygen bottles. Sun Wukong could carry at most 5 oxygen bottles at the same time.

‘P‘ : A room with unlimited number of speed-up pills. Every time Sun Wukong entered a ‘P‘ room from other rooms, he would get a speed-up pill. But staying there would not get Sun Wukong more speed-up pills. Sun Wukong could bring unlimited number of speed-up pills with him.

Sun Wukong could move in the palace. For each move, Sun Wukong might go to the adjacent rooms in 4 directions(north, west,south and east). But Sun Wukong couldn‘t get into a ‘#‘ room(deadly gas room) without an oxygen bottle. Entering a ‘#‘ room each time would cost Sun Wukong one oxygen bottle.

Each move took Sun Wukong one minute. But if Sun Wukong ate a speed-up pill, he could make next move without spending any time. In other words, each speed-up pill could save Sun Wukong one minute. And if Sun Wukong went into a ‘#‘ room, he had to stay there for one extra minute to recover his health.

Since Sun Wukong was an impatient monkey, he wanted to save Tang Monk as soon as possible. Please figure out the minimum time Sun Wukong needed to reach Tang Monk. Input There are no more than 25 test cases.

For each case, the first line includes two integers N and M(0 < N,M ≤ 100), meaning that the palace is a N × M matrix.

Then the N×M matrix follows.

The input ends with N = 0 and M = 0. Output For each test case, print the minimum time (in minute) Sun Wukong needed to save Tang Monk. If it‘s impossible for Sun Wukong to complete the mission, print -1 Sample Input 2 2
S#
#T
2 5
SB###
##P#T
4 7
SP.....
P#.....
......#
B...##T
0 0 Sample Output -1
8
11 題意 給一個100x100的迷宮,‘.‘表示路面,‘S‘表示起點,‘T‘表示終點;‘#‘表示毒氣區,進入毒氣區必須要消耗一個氧氣;‘B‘表示氧氣區,每次進入自動獲得一個氧氣,可反復進入從而獲得多個,但最多攜帶5個;‘P‘表示加速藥,獲得原理和氧氣一樣,使用後使下一次移動不耗時,可以無限攜帶。一次移動可以移動到相鄰的四個格子,花費一個單位時間,如果移動到了毒氣區,將在毒氣區額外停留一個單位時間。求從S到T的最短時間,如果不能到達,輸出-1。 分析 加速藥拿到立刻用掉和留著後面用的效果是一樣的,所以完全不必考慮加速藥的存在。
氧氣數量是這道題的關鍵,所以把狀態定義為(x, y, n),表示在(x,y)時還有n個氧氣,當氧氣用完時,就不能向毒氣區轉移了;同時我們還希望求出的最短時間,所以dp[x][y][n]=t,表示從起點出發到達狀態(x, y, n)花費的最少時間,這樣以後,如果終點是(tx,ty),那麽只要dp[tx][ty][i],(i=0,1,2,3,4,5)中任何一個不是無窮大,就是可以到達終點的。
接下來是狀態之間的轉移:
(x, y, n)可以向四個方向轉移,假設下一個地方是(tx,ty),那麽:
(tx,ty)是‘.‘或‘S‘,就用dp[x][y][n]+1更新dp[tx][ty][n];
(tx,ty)是‘T‘,同樣用dp[x][y][n]+1更新dp[tx][ty][n],並且不再向下轉移;
(tx,ty)是‘B‘,氧氣數量增加,如果n<5,那麽還可以拿氧氣,用dp[x][y][n]+1更新dp[tx][ty][n+1],否則更新dp[tx][ty][n];
(tx,ty)是‘P‘,下一步不耗時,用dp[x][y][n]更新dp[tx][ty][n];
(tx,ty)是‘#‘,氧氣數量減少,只有當n>0時,才可以進毒氣區,因為要額外花費一個單位時間,用dp[x][y][n]+2更新dp[tx][ty][n-1]。
那麽所有的轉移都搞定了,初始狀態很簡單,假設起點是(sx,sy),那麽就是dp[sx][sy][0]=0,其他所有的狀態都是INF。只要把所有可能到達的狀態更新了,那麽答案就在dp[tx][ty][i]中取最小就行了。
需要註意的是,狀態的更新需要用類似於BFS的順序,不斷的用已知的最優狀態,去更新相鄰的未知狀態,直至遍歷完所有的狀態,復雜度為O(5nm)。 總結 網格題真的是一點想法也沒有,看數據量也不大,就試著寫了個狀態推一推,沒想到真的推出來了。經大佬指點用spfa也可以搞,等學會了再補題把。 代碼
#include<stdio.h>
#include<memory.h>
#include<queue>
using std::queue;
char g[105][105];
int dp[105][105][10];//從起點出發直到在(x,y)拿著n個氧氣罐所需最少時間
#define INF 1000000
int nx[] = { -1,0,1,  0 };
int ny[] = {   0,1,0,-1 };
struct state
{
    int x, y, n;
    state(int _i=0,int _j=0,int _x=0):x(_i),y(_j),n(_x){}
};

int main()
{
    int n, m;
    int sx=0, sy=0, ex=0, ey=0;
    while(EOF!=scanf("%d %d",&n,&m)&&n&&m){
        for(int i=1;i<=n;++i){
            scanf("%s", g[i] + 1);
            g[i][0] = $;
            for(int j=1;j<=m;++j){
                if (g[i][j] == S) { sx = i; sy = j; }
                else if (g[i][j] == T) { ex = i; ey = j; }
                //輸入時順帶初始化dp數組
                for (int t = 0; t <= 5; ++t)dp[i][j][t] = INF;
            }
        }
        //起始狀態
        dp[sx][sy][0] = 0;
        //bfs的順序更新
        queue<state> mq;
        mq.push(state(sx, sy, 0));
        state cur;
        int tx, ty;
        while(!mq.empty())    {
            cur = mq.front(); mq.pop();
            for(int k=0;k<4;++k)//四個轉移方向
            {
                tx = cur.x + nx[k];
                ty = cur.y + ny[k];
                //不在地圖內
                if (tx<1 || tx>n || ty<1 || ty>m)continue;

                switch(g[tx][ty]){
                    case S:
                    case .:
                        if (dp[tx][ty][cur.n] > dp[cur.x][cur.y][cur.n] + 1){
                            dp[tx][ty][cur.n] = dp[cur.x][cur.y][cur.n] + 1;
                            mq.push(state(tx, ty, cur.n));
                        }
                        break;
                    case T:
                        //不再向下轉移
                        if (dp[tx][ty][cur.n] > dp[cur.x][cur.y][cur.n] + 1)
                            dp[tx][ty][cur.n] = dp[cur.x][cur.y][cur.n] + 1;
                        break;
                    case P:
                        if (dp[tx][ty][cur.n] > dp[cur.x][cur.y][cur.n]){
                            dp[tx][ty][cur.n] = dp[cur.x][cur.y][cur.n];
                            mq.push(state(tx, ty, cur.n));
                        }
                        break;
                    case B:
                        if (cur.n<5&&dp[tx][ty][cur.n + 1] > dp[cur.x][cur.y][cur.n] + 1){
                            dp[tx][ty][cur.n + 1] = dp[cur.x][cur.y][cur.n] + 1;
                            mq.push(state(tx, ty, cur.n +1));
                        }
                        else if(cur.n==5&&dp[tx][ty][cur.n] > dp[cur.x][cur.y][cur.n] + 1){
                            dp[tx][ty][cur.n] = dp[cur.x][cur.y][cur.n] + 1;
                            mq.push(state(tx, ty, cur.n));
                        }
                        break;
                    case #:
                        if (cur.n>0&&dp[tx][ty][cur.n - 1] > dp[cur.x][cur.y][cur.n] + 2){
                            dp[tx][ty][cur.n - 1] = dp[cur.x][cur.y][cur.n] + 2;
                            mq.push(state(tx, ty, cur.n -1));
                        }
                        break;
                    default:
                        break;
                    }

            }
        }
        int ans = INF;
        for(int i=0;i<=5;++i){
            if (ans > dp[ex][ey][i])ans = dp[ex][ey][i];
        }
        if (ans >= INF)printf("-1\n");
        else printf("%d\n", ans);
    }
}

hihocoder 1828 Saving Tang Monk II (DP+BFS)