ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 A、Saving Tang Monk II (BFS)

练习BFS好题

题目链接:http://hihocoder.com/problemset/problem/182

题目1 : Saving Tang Monk II

时间限制:1000ms

单点时限:1000ms

内存限制:256MB

 

描述

《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.

输入

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.

输出

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

样例输入

2 2
S#
#T
2 5
SB###
##P#T
4 7
SP.....
P#.....
......#
B...##T
0 0

样例输出

-1
8

11

题意

给一个100x100的迷宫,'.'表示路面,'S'表示起点,'T'表示终点;'#'表示毒气区,进入毒气区必须要消耗一个氧气;'B'表示氧气区,每次进入自动获得一个氧气,可反复进入从而获得多个,但最多携带5个;'P'表示加速药,获得原理和氧气一样,使用后使下一次移动不耗时,可以无限携带。一次移动可以移动到相邻的四个格子,花费一个单位时间,如果移动到了毒气区,将在毒气区额外停留一个单位时间。求从S到T的最短时间,如果不能到达,输出-1。

分析


氧气数量是这道题的关键,所以把状态定义为(x, y, n),表示在(x,y)时还有n个氧气,当氧气<=0,就不能向毒气区转移了;同时我们还希望求出的最短时间,所以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)。

AC代码:

//也就是普通的二维BFS多加了一个 氧气瓶 B 的变量与 毒气的关系
#include<bits/stdc++.h>
using namespace std;
const int  N=105;
const int M=0x3f3f3f3f;
int dp[N][N][8];
char mp[N][N];
int d[2][4]= {0,1,0,-1,1,0,-1,0};
struct node
{
  int x,y,n;//位置 x y 氧气瓶数 n
};
int main()
{
  int i,j,n,m,t;
  int sx,sy,ex,ey;//起点 终点
  while(~scanf("%d %d",&n,&m))
  {
    if(!n&&!m) break;
    for(int i=1; i<=n; i++)
    {
      scanf("%s",mp[i]+1);
      for(int j=1; j<=m; j++)
      {
        if(mp[i][j]=='S') sx=i,sy=j;
        else if(mp[i][j]=='T') ex=i,ey=j;
        for(int k=0; k<=5; k++) dp[i][j][k]=M;//初始化
      }
    }
    queue<node>q;
    dp[sx][sy][0]=0;
    q.push(node{sx,sy,0});
    while(!q.empty())//BFS
    {
      node t=q.front();
      q.pop();

      for(int i=0; i<4; i++)//四个方向
      {
        int x=t.x+d[1][i];
        int y=t.y+d[0][i];
        int c=t.n;
        if(x>n||y>m||x<1||y<1) continue;//出界
        switch(mp[x][y])
        {

        case 'S'://起点
        case '.'://空地
          if(dp[x][y][c]>dp[t.x][t.y][c]+1)//秒数比较
          {
            dp[x][y][c]=dp[t.x][t.y][c]+1;
            q.push(node{x,y,c});
          }
          break;
        case 'T'://终点
          if(dp[x][y][c]>dp[t.x][t.y][c]+1)//秒数比较
          {
            dp[x][y][c]=dp[t.x][t.y][c]+1;
            q.push(node{x,y,c});
          }
          break;
        case '#'://毒气
          if(c>0&&dp[x][y][c-1]>dp[t.x][t.y][c]+2)//氧气瓶数>0 氧气瓶-1  花费秒数+2
          {
            dp[x][y][c-1]=dp[t.x][t.y][c]+2;
            q.push(node{x,y,c-1});
          }
          break;
        case 'B'://氧气
          if(c<5&&dp[x][y][c+1]>dp[t.x][t.y][c]+1)//氧气瓶+1 秒数+1
          {
            dp[x][y][c+1]=dp[t.x][t.y][c]+1;
            q.push(node{x,y,c+1});
          }
          else if(c==5&&dp[x][y][c]>dp[t.x][t.y][c]+1)//不拿氧气瓶 比较秒数
          {
            dp[x][y][c]=dp[t.x][t.y][c]+1;
            q.push(node{x,y,c});
          }
          break;
        case 'P'://加速
          if(dp[x][y][c]>dp[t.x][t.y][c]) //加速 不用花费秒数
          {
            dp[x][y][c]=dp[t.x][t.y][c];
            q.push(node{x,y,c});
          }
          break;
        default://其他
          break ;

        }
      }
    }
    int ans=M;
    for(int k=0; k<=5; k++)//寻找终点最小值
      ans=min(ans,dp[ex][ey][k]);
    
    if(ans>=M) printf("-1\n");//到达不了
    else printf("%d\n",ans);
  }
}

AC代码2:

#include<bits/stdc++.h>
using namespace std;
bool vis[105][105][6];
int n,m;
struct Node 
{
    int x,y,ox,step;
    Node() {}
    Node(int _x,int _y,int _ox,int _step) 
    {
        x=_x;
        y=_y;
        ox=_ox;
        step=_step;
    }
    bool operator<(const Node& e) const {return step>e.step;}
};
char ch[105][105];
priority_queue<Node> Q;
int p[4]={0,0,1,-1};
int q[4]={1,-1,0,0};
int main() 
{
    while (scanf("%d%d",&n,&m)) 
    {
        if (n==0&&m==0) break;
        for (int i=0;i<n;i++) scanf("%s",ch[i]);
        int sx,sy,ex,ey;
        for (int i=0;i<n;i++)
            for (int j=0;j<m;j++)
            {
                if(ch[i][j]=='S') 
                {
                    sx=i;sy=j;
                }else 
                if(ch[i][j]=='T') 
                {
                    ex=i;ey=j;
                }
            }
        while(!Q.empty()) Q.pop();
        Node start(sx,sy,0,0);
        memset(vis,0,sizeof(vis));
        vis[sx][sy][0]=1;
        Q.push(start);
        bool ok=false;
        while(!Q.empty()) 
        {
            Node node=Q.top();
            Q.pop();
            if (node.x==ex&&node.y==ey) 
            {
                ok=true;
                printf("%d\n",node.step);
                break;
            }
            for (int i=0;i<=3;i++)
            {
                int xx=node.x+p[i],yy=node.y+q[i];
                if (xx<0 || xx>=n || yy<0 || yy>=m) continue;
                int step=node.step;
                if(ch[xx][yy]=='B') 
                {
                    int _ox=min(5,node.ox+1);
                    if (vis[xx][yy][_ox]) continue;
                    vis[xx][yy][_ox]=1;
                    Node temp(xx,yy,_ox,step+1);
                    Q.push(temp);
                }else 
                if(ch[xx][yy]=='#') 
                {
                    int _ox=node.ox;
                    if (!_ox) continue;
                    _ox--;
                    if(vis[xx][yy][_ox]) continue;
                    vis[xx][yy][_ox]=1;
                    Node temp(xx,yy,_ox,step+2);
                    Q.push(temp);
                }else 
                if(ch[xx][yy]=='P') 
                {
                    int _ox=node.ox;
                    if(vis[xx][yy][_ox]) continue;
                    vis[xx][yy][_ox]=1;
                    Node temp(xx,yy,_ox,step);
                    Q.push(temp);
                }else 
                {
                    int _ox=node.ox;
                    if(vis[xx][yy][_ox]) continue;
                    vis[xx][yy][_ox]=1;
                    Node temp(xx,yy,_ox,step+1);
                    Q.push(temp);
                }
            }
        }
        if (!ok) puts("-1");
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值