BFS-混境之地4、6

混境之地4

题目描述

小蓝有一天误入了一个混境之地。他拿到了一张地图,并从中获取到以下信息:

  • 混境之地的大小为 n × m n \times m n×m,其中 # 表示不可通过的墙壁,. 表示可以走的路,V 表示圣泉。
  • 小蓝当前所在位置的坐标为 ( A , B ) (A, B) (A,B),而这个混境之地出口的坐标为 ( C , D ) (C, D) (C,D),当站在出口时即表示可以逃离混境之地。

好消息是:他可以通过圣泉恢复体力。坏消息是:小蓝仅剩下 E E E 点能量,能量值不可小于 0。每行走一次消耗一点能量,耗时一分钟,在圣泉处每一分钟可以恢复一点体力,无上限。

小蓝想知道他能否逃离这个混境之地,如果可以逃离这里,则输入所需要的最少时间,反之输出 No

输入格式

  • 第一行输入两个正整数 n , m n, m n,m,表示混境之地的大小。
  • 第二行输入四个正整数 A , B , C , D A, B, C, D A,B,C,D,表示小蓝当前所在位置的坐标,以及混境之地出口的坐标。
  • 第三行至第 n + 2 n+2 n+2 行,每行 m m m 个字符,表示混境之地的地图,其中 # 表示不可通过的墙壁,. 表示普通的道路,V 表示圣泉。
  • 最后一行一个正整数 E E E,代表剩余的能量值。

输出格式

输出数据共一行:

  • 若小蓝可以逃离混境之地,则输出所需要的最少时间。
  • 若小蓝无法逃离混境之地,则输出 No

输入输出样例

样例输入 1

5 5
1 1 5 5
...#.
..#..
#....
V..#.
...#.
5

样例输出 1

19

样例解释 1

(1, 1)(5, 5) 的一条可行道路为:(1, 1) → (1, 2) → (2, 2) → (3, 2) → (4, 2) → (4, 1) 休息七个单位时间后 → (4, 2) → (4, 3) → (3, 3) → (3, 4) → (3, 5) → (4, 5) → (5, 5)

样例输入 2

5 5
1 1 5 5
...#.
..#..
#....
V..##
...#.
200

样例输出 2

No

样例解释 2

不存在一条合法路径从起点到终点。

数据范围

对于所有测试样例:

  • 1 ≤ n , m ≤ 1000 1 \leq n, m \leq 1000 1n,m1000
  • 1 ≤ E ≤ 1 0 6 1 \leq E \leq 10^6 1E106

数据保证起点和终点一定为普通道路。

题解

为了高效地解决这个问题,我们可以使用广度优先搜索(BFS)来计算从起点到每个点的距离以及从终点到每个点的距离。然后,我们检查是否有足够的能量到达终点或经过圣泉恢复能量后到达终点。

代码实现

#include
using namespace std;
using ll = long long;

const int N = 1e3 + 9;
const int MAX_INF = 0x3f3f3f3f;

char mp[N][N];
int n, m, a, b, c, d, E;

int pre[N][N], nex[N][N]; // 分别存储从起点和终点出发到每个点的最短距离
int dx[] = {1, 0, -1, 0}; // 方向数组
int dy[] = {0, 1, 0, -1};

vector<pair<int, int>> v; // 存储所有圣泉的位置

struct node {
    int cx, cy, d; // 当前坐标和距离
};

bool vis[N][N];

// 广度优先搜索计算最短路径
void bfs(int x, int y, int dis[N][N]){
    queue<node> q;
    q.push({x, y, 0});
    dis[x][y] = 0;
    while(!q.empty()){
        node cq = q.front(); q.pop();
        dis[cq.cx][cq.cy] = cq.d;
        for(int i = 0; i < 4; i++){
            int nx = cq.cx + dx[i], ny = cq.cy + dy[i];
            if(nx < 1 || ny < 1 || nx > n || ny > m) continue;
            if(mp[nx][ny] == '#') continue;
            if(!vis[nx][ny]){
                vis[nx][ny] = true;
                q.push({nx, ny, cq.d + 1});
            }
        }
    }
}

int main(){
    ios::sync_with_stdio(0), 
    cin.tie(0), cout.tie(0);
    cin >> n >> m;
    cin >> a >> b >> c >> d;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++){
            cin >> mp[i][j];
            if(mp[i][j] == 'V'){
                v.push_back({i, j}); // 记录所有圣泉的位置
            }
        }

    cin >> E;
    
    memset(pre, 0x3f, sizeof pre); // 初始化距离数组
    memset(nex, 0x3f, sizeof nex);
    
    vis[a][b] = true;
    bfs(a, b, pre); // 从起点开始BFS
    memset(vis, false, sizeof vis);
    vis[c][d] = true;
    bfs(c, d, nex); // 从终点开始BFS
    
    int ans = MAX_INF;
    
    // 如果可以直接到达终点
    if(E >= pre[c][d]){
        if(pre[c][d] == MAX_INF) cout << "No" << '\n';
        else cout << pre[c][d] << '\n';
        return 0;
    }
    
    // 检查是否可以通过圣泉恢复能量后到达终点
    for(const auto &p : v){
        int x = p.first, y = p.second;
        if(pre[x][y] <= E){
            ans = min(ans, pre[x][y] + nex[x][y]);
        }
    }
    
    if(ans != 0x3f3f3f3f){
        cout << E + (ans - E) * 2 << '\n'; // 计算总时间
    } else {
        cout << "No" << '\n';
    }
    return 0;
}

此代码通过两次广度优先搜索(BFS)分别计算从起点和终点出发到每个点的最短距离。首先检查是否可以直接到达终点,如果不能,则检查是否可以通过圣泉恢复能量后到达终点。这种方法能够有效地处理大规模数据,并确保在有限的时间内找到最优解。

此题的关键在计算起点和终点分别到每一个点的最短距离并预处理,可以轻易获取到如果要经过这个点时从起点到终点的最短距离, 在预处理时注意细节

以下是另一个类似题目

混境之地6

题目描述

小蓝有一天误入了一个混境之地。他拿到了一张地图,并从中获取到以下信息:

  • 混境之地是一个 n × m n \times m n×m 大小的矩阵,其中 # 表示墙,无法通行,. 表示普通的道路,k 表示散落在地图中的钥匙。
  • 小蓝当前所在位置的坐标为 ( A , B ) (A, B) (A,B),而这个混境之地出口的坐标为 ( C , D ) (C, D) (C,D),当站在出口时且携带最少一把钥匙即表示可以逃离混境之地。

好消息是:地图中可能存在不止一把钥匙。坏消息是:地图中可能没有钥匙。小蓝可以往上下左右四个方向行走,每走一步耗时一分钟。

小蓝想知道他能否逃离这个混境之地,如果可以逃离这里,则输出最少需要消耗多少时间,反之输出 -1

输入格式

  • 第一行输入两个正整数 n , m n, m n,m,表示矩阵的大小。
  • 第二行输入四个正整数 A , B , C , D A, B, C, D A,B,C,D,表示小蓝当前所在位置的坐标,以及混境之地出口的坐标。
  • 接下来输入 n n n 行,每行 m m m 个字符,表示混境之地的地图,其中 # 表示墙,无法通行,. 表示普通的道路,k 表示散落在地图中的钥匙。

输出格式

输出数据共一行一个字符串:

  • 若小蓝可以逃离混境之地,则输出最少需要消耗多少时间。
  • 若小蓝无法逃离混境之地,则输出 -1

输入输出样例

样例输入 1

5 5
1 1 5 5
....#
#.#..
k.#..
.#...
...#.

样例输出 1

12

样例解释 1

(1, 1)(5, 5) 的最短道路为:

  • (1, 1) → (1, 2) → (2, 2) → (3, 2) → (3, 1) 拿到钥匙。
  • (3, 1) → (4, 1) → (5, 1) → (5, 2) → (5, 3) → (4, 3) → (4, 4) → (4, 5) → (5, 5) 到达终点。

样例输入 2

5 5
1 1 5 5
....#
#.##.
k.#..
##...
...#.

样例输出 2

-1

样例解释 2

可以证明不存在一条路径可以从起点到达终点。

数据范围

对于所有测试样例:

  • 1 ≤ n , m ≤ 1000 1 \leq n, m \leq 1000 1n,m1000

数据保证起点和终点一定为普通道路。

题解

为了高效地解决这个问题,我们可以使用广度优先搜索(BFS)来计算从起点到每个点的距离以及从终点到每个点的距离。然后,我们检查是否存在一条路径可以通过至少一把钥匙并最终到达终点。

代码实现

#include
using namespace std;
using ll = long long;

const int N = 1e3 + 9, MAX_INF = 0x3f3f3f3f;

char mp[N][N];

int n, m, a, b, c, d, pre[N][N], nex[N][N], dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};

bool vis[N][N];

struct node {
    int x, y, d; // 当前坐标和距离
};

// 广度优先搜索计算最短路径
void bfs(int x, int y, int dis[N][N]){
    queue<node> q;
    q.push({x, y, 0});
    while(!q.empty()){
        node cq = q.front(); q.pop();
        vis[cq.x][cq.y] = true;
        dis[cq.x][cq.y] = cq.d;
        for(int i = 0; i < 4; i++){
            int nx = cq.x + dx[i], ny = cq.y + dy[i];
            if(nx < 1 || ny < 1 || nx > n || ny > m) continue;
            if(mp[nx][ny] == '#') continue;
            if(!vis[nx][ny]){
                vis[nx][ny] = true;
                q.push({nx, ny, cq.d + 1});
            }
        }
    }
}

vector<pair<int, int>> v; // 存储所有钥匙的位置

int main(){
    ios::sync_with_stdio(0), 
    cin.tie(0), cout.tie(0);
    cin >> n >> m;
    cin >> a >> b >> c >> d;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++){
            cin >> mp[i][j];
            if(mp[i][j] == 'k') v.push_back({i, j}); // 记录所有钥匙的位置
        }

    memset(pre, 0x3f, sizeof pre); // 初始化距离数组
    memset(nex, 0x3f, sizeof nex);
    memset(vis, false, sizeof vis);

    bfs(a, b, pre); // 从起点开始BFS
    memset(vis, false, sizeof vis);
    bfs(c, d, nex); // 从终点开始BFS
    
    if(v.size() == 0){
        cout << -1 << '\n';
        return 0;
    }
    
    int ans = MAX_INF;
    
    // 检查是否可以通过某个钥匙位置到达终点
    for(const auto &p : v){
        int x = p.first, y = p.second;
        if(pre[x][y] == MAX_INF || nex[x][y] == MAX_INF) continue;
        ans = min(ans, pre[x][y] + nex[x][y]);
    }
    
    if(ans == MAX_INF){
        cout << -1 << '\n';
    } else {
        cout << ans << '\n';
    }
    return 0;
}

此代码通过两次广度优先搜索(BFS)分别计算从起点和终点出发到每个点的最短距离。首先记录所有钥匙的位置,然后检查是否有路径可以通过至少一把钥匙并最终到达终点。这种方法能够有效地处理大规模数据,并确保在有限的时间内找到最优解。如果没有钥匙或无法通过任何钥匙到达终点,则输出 -1

你可能感兴趣的:(宽度优先,算法,数据结构)