2021-08-31 AcWing暑期最后一题 3825. 逃离大森林

你是一个宝可梦饲养员,你正在进行你的冒险之旅。

当前,你的目标是逃离飞鸟森林。

飞鸟森林可以表示为一个 r×c 的方格矩阵。

每个方格,要么是树木,要么是空地。

空地中可能包含 0 个或多个宝可梦饲养员(森林中可能存在除你以外的其他饲养员)。

所有饲养员(包括你在内)都不能进入到包含树木的方格之中。

有一个方格为出口方格,到达这里就可以逃离大森林。

出口方格一定是空地。

最初始的方格矩阵将提供给你,这包含了你的初始位置,出口方格的位置、空地和树木的分布以及其他所有饲养员的位置,

下图是一个初始方格矩阵的示例:

1.png

所有饲养员(包括你)都可以在森林中进行移动。

每次移动可以:

  • 原地不动。也就是放弃本次移动。
  • 沿上下左右四个方向移动一格距离。

注意,任何人都不可能移动至带有树木的方格中。

如果你在一次移动中,抵达了出口方格,那么下次移动你就可以选择离开森林。

其他饲养员不会以这种方式主动离开森林。

在你进行一次移动时,其他所有饲养员也会同时进行一次移动(每个饲养员的移动方式可能不尽相同)。

当你和 t个其他饲养员位于同一方格之中时,就会和他们进行 t 场一对一的战斗(战斗时间忽略不计)。

所有跟你战斗过的饲养员都会失去所有战斗力,无法再挑战你,只能饮恨离开森林。

注意,在你离开森林的那次移动中,即使有饲养员在此次移动中抵达出口方格,你也不会和他们发生战斗。

另外,其他饲养员只会和你发生战斗,其他饲养员之间不会发生战斗(可能有多个饲养员位于同一方格中)。

既然要离开森林,你就需要在开始行动之前制定一条行进路线,确定你的一系列移动。

在确定这个具体行动计划后,你一定会严格遵循制定好的路线开展你的所有移动。

并且你还会将你的行动路线提前公开发布到博客上。

其他所有饲养员都关注了你的博客,并且迫切的渴望与你战斗。

因为已经提前知道了你的所有移动顺序,所以其他饲养员当中,有机会在你的行进过程中碰到你的,都会主动撞上你,以确保能够和你战斗,而无论如何也碰不到你的,就会原地不动,不白费力气。

你是个不喜欢战斗的人,所以你想要制定一条最为合理的行进路线,使得你在逃离大森林的过程中进行战斗的场次尽可能少。

注意,为了达到这一目的,即使行进路线长于最短行进路线也在所不惜。

输入格式

第一行包含两个整数 r 和 c。

接下来 r 行,每行包含一个长度为 c 的字符串,表示森林矩阵。每个字符可能是:

  • T:表示树木方格。
  • S:表示你的初始位置方格,这是一个空地,保证有且仅有一个。
  • E:表示出口方格,这是一个空地,保证有且仅有一个。
  • 一个数字(0∼9):表示空地方格,数字为 x 则表示该空地上有 x 个饲养员。

保证你一定可以从初始位置走到出口方格位置。

输出格式

输出一个整数,表示你最少需要进行的战斗场次。

数据范围

1≤r,c≤10001≤r,c≤1000

输入样例1:

5 7
000E0T3
T0TT0T0
010T0T0
2T0T0T0
0T0S000

输出样例1:

3

样例1解释

森林方格矩阵如下所示:

2.png

蓝色线路即为你可以选择的行进路线,在该行进路线中,只有左下方的三人可以撞到你,而右上角的三人无法撞到你,所以只需要进行三场战斗。

输入样例2:

1 4
SE23

输出样例2:

2

样例2解释

森林方格矩阵如下所示:

3.png

你在第一次移动中,直接移动至出口方格处:

4.png

与此同时,右边的两个人也移动至出口方格处,你们发生战斗(最右边的三人因为赶不上你,所以原地不动):

5.png

你把两个挑战者干跑了:

6.png

在第二次移动中,你选择离开森林:

7.png


 思路:

竞争者都会接近你的逃跑路线,乃至稳合,那么如果他们可以在你逃出之前和你打架的话,他们到出口的最短路径一定小于等于你的最短路径

因此,问题转化为:找到那些和你同时到或者比你先到出口的人即可

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

typedef pair<int, int> PII;//竞争者坐标
const int N = 1010;

int res=0;
int m,n,sx,sy;
int step[N][N];
char p[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int bfs()
{
    memset(step, 0x3f, sizeof step);
    for (int i = 0; i < m; i ++ )
        for (int j = 0; j < n; j ++ )
            if (p[i][j] == 'E')
                sx = i, sy = j;
    
    queue<PII> q;
    q.push({sx, sy});
    step[sx][sy] = 0;
    
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        
        for (int i = 0; i < 4; i ++ ){
            int x = t.first + dx[i], y = t.second + dy[i];
            if(x>=0 && x<m && y>=0 && y<n && p[x][y]!='T'){
                if(step[x][y] > step[t.first][t.second]+1){
                    step[x][y] = step[t.first][t.second]+1;
                    q.push({x,y});
                }
            }
        }
    }
    for (int i = 0; i < m; i ++ )
        for (int j = 0; j < n; j ++ )
            if (p[i][j] == 'S')
                return step[i][j];
                
    return -1;
}
int main()
{
    cin >> m >> n;
    for (int i = 0; i < m; i ++ ) scanf("%s", p[i]);
    
    int res = 0;
    int sum = bfs();
    for (int i = 0; i < m; i ++ )
    {
        for (int j = 0; j < n; j ++ )
        {
            if(p[i][j]>'0' && p[i][j]<='9' && step[i][j]<=sum){
                res += p[i][j]-'0';
            }
        }
    }
    cout << res;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泥烟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值