【BZOJ 4394】[Usaco2015 dec] Bessie(bfs)

在这款游戏中,奶牛Bessie被困在一个N*M的网格迷宫中,需要从左上角到达右下角。每块地砖的颜色决定了其特性,如是否可通行、是否带有气味等。Bessie需要利用这些特性,找到从起点到终点的最短路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

Description

After eating too much fruit in Farmer John’s kitchen, Bessie the cow is getting some very strange dreams! In her most recent dream, she is trapped in a maze in the shape of an N×M grid of tiles (1≤N,M≤1,000). She starts on the top-left tile and wants to get to the bottom-right tile. When she is standing on a tile, she can potentially move to the adjacent tiles in any of the four cardinal directions.

But wait! Each tile has a color, and each color has a different property! Bessie’s head hurts just thinking about it:

If a tile is red, then it is impassable.
If a tile is pink, then it can be walked on normally.
If a tile is orange, then it can be walked on normally, but will make Bessie smell like oranges.
If a tile is blue, then it contains piranhas that will only let Bessie pass if she smells like oranges.
If a tile is purple, then Bessie will slide to the next tile in that direction (unless she is unable to cross it). If this tile is also a purple tile, then Bessie will continue to slide until she lands on a non-purple tile or hits an impassable tile. Sliding through a tile counts as a move. Purple tiles will also remove Bessie's smell. 

(If you’re confused about purple tiles, the example will illustrate their use.)

Please help Bessie get from the top-left to the bottom-right in as few moves as possible.

奶牛Bessie被困在了N*M的网格图迷宫中,她位于左上角(1,1),出口在右下角(N,M)。Bessie只能上下左右行走。

每块地砖都有一个颜色:

如果是红色,那么不可通行。

如果是粉色,那么可以通行。

如果是橙色,那么可以通行,但是会给Bessie带上橘子的气味。

如果是蓝色,那么当且仅当Bessie带着橘子的气味时,才可以通行。

如果是紫色,那么Bessie会保持原有方向滑过去,如果之后仍然是紫色,那么会继续滑。当滑到不是紫色的地砖上或者不可通行的时候,才会停下来。并且这会消除Bessie身上的气味。每一步滑行和走路一样,都需要耗费一单位时间。

请输出Bessie逃到出口所需的最短时间。

Input

The first line has two integers N and M, representing the number of rows and columns of the maze.

The next NN lines have MM integers each, representing the maze:

The integer ‘0’ is a red tile
The integer ‘1’ is a pink tile
The integer ‘2’ is an orange tile
The integer ‘3’ is a blue tile
The integer ‘4’ is a purple tile

The top-left and bottom-right integers will always be ‘1’.

Output

A single integer, representing the minimum number of moves Bessie must use to cross the maze, or -1 if it is impossible to do so.

Sample Input

4 4
1 0 2 1
1 1 4 1
1 0 4 0
1 3 1 1

Sample Output

10

HINT

In this example, Bessie walks one square down and two squares to the right (and then slides one more square to the right). She walks one square up, one square left, and one square down (sliding two more squares down) and finishes by walking one more square right. This is a total of 10 moves (DRRRULDDDR).

思路

Big, Supreme, Super, Enormous 的 BFS
首先确定结构体状态。

struct node {
    int x, y, k, d, f;
};

分别维护坐标,方向,橙子味,距离。
注意:因为数组大小不确定,所以要用 stlstlstlqueuequeuequeue,手写队列会WAWAWA
还有,当 a=4a=4a=4 时,由于不同的方向会导致不同的结果。所以要把方向记录下来。
总之,很麻烦。
主要还是我太菜了才觉得麻烦吧……

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
const int maxn = 1010;
int wx[5] = {0, 1, -1, 0, 0}, wy[5] = {0, 0, 0, 1, -1};
struct node {
    int x, y, k, d;
    bool f;
}; queue <node> Q;
int n, m, a[maxn][maxn];
bool b[maxn][maxn][2][4];

inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1)+(x << 3)+(ch ^ 48);
        ch = getchar();
    }
    return x*f;
}

int main() {
	ios::sync_with_stdio(false);
    n = read(); m = read();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) a[i][j] = read();
    Q.push((node) {1, 1, 0, 0, 0});
    while (!Q.empty()) {
        node temp = Q.front();
        Q.pop();
        int x = temp.x, y = temp.y, k = temp.k, d = temp.d;
        bool f = temp.f, flag = 0;
        if (b[x][y][f][d]) continue;
        b[x][y][f][d] = 1;
        if (x == n && y == m) {
            printf("%d\n", k);
            exit(0);
        }
        if (a[x][y] == 4) {
            int xx = x + wx[d], yy = y + wy[d];
            if (!a[xx][yy] || a[xx][yy] == 3);
            else if (a[xx][yy] == 1 || a[xx][yy] == 4)
                Q.push((node) {xx, yy, k + 1, d, 0}), flag = 1;
            else Q.push((node) {xx, yy, k + 1, d, 1}), flag = 1;
        }
        if (a[x][y] == 4 && flag) continue;
        for (int i = 1; i <= 4; i++) {
            int xx = x + wx[i], yy = y + wy[i];
            if (!a[xx][yy] || (a[xx][yy] == 3 && !f)) continue;
            else if (a[xx][yy] == 1 || (a[xx][yy] == 3 && f) || a[xx][yy] == 4)
                Q.push((node) {xx, yy, k + 1, i, f});
            else if (a[xx][yy] == 2) Q.push((node) {xx, yy, k + 1, i, 1});
        }    
    }
    cout << -1 << endl;
    return 0;
}
题目描述 牛牛和她的朋友们正在玩一个有趣的游戏,他们需要构建一个有 $n$ 个节点的无向图,每个节点都有一个唯一的编号并且编号从 $1$ 到 $n$。他们需要从节点 $1$ 到节点 $n$ 找到一条最短路径,其中路径长度是经过的边权的和。为了让游戏更有趣,他们决定在图上添加一些额外的边,这些边的权值都是 $x$。他们想知道,如果他们添加的边数尽可能少,最短路径的长度最多会增加多少。 输入格式 第一行包含两个正整数 $n$ 和 $m$,表示节点数和边数。 接下来 $m$ 行,每行包含三个整数 $u_i,v_i,w_i$,表示一条无向边 $(u_i,v_i)$,权值为 $w_i$。 输出格式 输出一个整数,表示最短路径的长度最多会增加多少。 数据范围 $2 \leq n \leq 200$ $1 \leq m \leq n(n-1)/2$ $1 \leq w_i \leq 10^6$ 输入样例 #1: 4 4 1 2 2 2 3 3 3 4 4 4 1 5 输出样例 #1: 5 输入样例 #2: 4 3 1 2 1 2 3 2 3 4 3 输出样例 #2: 2 算法 (BFS+最短路) $O(n^3)$ 我们把问题转化一下,假设原图中没有添加边,所求的就是点 $1$ 到点 $n$ 的最短路,并且我们已经求出了这个最短路的长度 $dis$。 接下来我们从小到大枚举边权 $x$,每次将 $x$ 加入图中,然后再次求解点 $1$ 到点 $n$ 的最短路 $dis'$,那么增加的最短路长度就是 $dis'-dis$。 我们发现,每次加入一个边都需要重新求解最短路。如果我们使用 Dijkstra 算法的话,每次加入一条边需要 $O(m\log m)$ 的时间复杂度,总的时间复杂度就是 $O(m^2\log m)$,无法通过本题。因此我们需要使用更优秀的算法。 观察到 $n$ 的范围比较小,我们可以考虑使用 BFS 求解最短路。如果边权均为 $1$,那么 BFS 可以在 $O(m)$ 的时间复杂度内求解最短路。那么如果我们只是加入了一条边的话,我们可以将边权为 $x$ 的边看做 $x$ 条边的组合,每次加入该边时,我们就在原始图上添加 $x$ 条边,边权均为 $1$。这样,我们就可以使用一次 BFS 求解最短路了。 但是,我们不得不考虑加入多条边的情况。如果我们还是将边权为 $x$ 的边看做 $x$ 条边的组合,那么我们就需要加入 $x$ 条边,而不是一条边。这样,我们就不能使用 BFS 了。 但是,我们可以使用 Floyd 算法。事实上,我们每次加入边时,只有边权等于 $x$ 的边会发生变化。因此,如果我们枚举边权 $x$ 时,每次只需要将边权等于 $x$ 的边加入图中,然后使用 Floyd 算法重新计算最短路即可。由于 Floyd 算法的时间复杂度为 $O(n^3)$,因此总的时间复杂度为 $O(n^4)$。 时间复杂度 $O(n^4)$ 空间复杂度 $O(n^2)$ C++ 代码 注意点:Floyd算法计算任意两点之间的最短路径,只需要在之前的路径基础上加入新的边构成的新路径进行更新即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值