📜  门|门CS 2011 |问题 13(1)

📅  最后修改于: 2023-12-03 14:58:37.251000             🧑  作者: Mango

题目介绍

本题为“门|门CS 2011”中的第13题,是一道考察动态规划的问题。

题目描述:

有一个 $n\times m$ 的网格图,其中包含了若干个门和墙。起点为 $(x_1, y_1)$,终点为 $(x_2, y_2)$,不能经过墙,只能通过门进出,但一次只能通过一个门。每次通过门时,进出的位置在门的对面,即如果门在 $(x_3, y_3)$,那么进门的位置在 $(x_3,y_3)$ 的对面,出门的位置在 $(x_3,y_3)$ 的另一面。

请计算从起点到终点的最短距离。

思路分析

本题可以采用动态规划思想进行求解。建立 $f(i,j,k)$ 表示从起点到 $(i,j)$ 并且经过了第 $k$ 个门的最短路程。则有

$$f(i,j,k)=\min{f(x,y,k-1)+d((x,y),(i,j))|\forall\text{门}(x,y)}$$

其中 $d((x_1,y_1),(x_2,y_2))$ 表示两点之间的距离。需要注意的是,当门的位置就是 $(i,j)$ 时,$f(i,j,k)$ 为无穷大。

最终的答案为 $\min{f(x,y,K)+d((x,y),(x_2,y_2))|\forall\text{门}(x,y)}$。其中 $K$ 表示一共经过了几个门。

具体实现时,可以采用二维数组来记录状态,并采用队列进行广度优先搜索。

代码实现

以下为C++代码实现,供参考。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 110;
const int maxk = 110;

int n, m, sx, sy, tx, ty, k; // 定义变量

struct node { // 定义一个结构体
    int x, y, t;
};

int mp[maxn][maxn], f[maxn][maxn][maxk];
bool vis[maxn][maxn][maxk];

int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};

int bfs() { // 广度优先搜索
    memset(f, 0x3f, sizeof(f)); // 初始化
    memset(vis, 0, sizeof(vis));
    queue<node> q;
    q.push({sx, sy, 0});
    f[sx][sy][0] = 0;
    while (!q.empty()) {
        auto u = q.front();
        q.pop();
        auto x = u.x, y = u.y, t = u.t;
        vis[x][y][t] = false;
        for (int i = 0; i < 4; i++) { // 四个方向
            auto nx = x + dx[i], ny = y + dy[i];
            if (nx < 1 || nx > n || ny < 1 || ny > m) continue; // 越界
            auto nt = t + mp[nx][ny];
            if (nt > k) continue; // 次数超了
            if (f[nx][ny][nt] > f[x][y][t] + 1) { // 松弛
                f[nx][ny][nt] = f[x][y][t] + 1;
                if (!vis[nx][ny][nt]) { // 入队
                    vis[nx][ny][nt] = true;
                    q.push({nx, ny, nt});
                }
            }
        }
    }
    int ans = INT_MAX;
    for (int i = 0; i <= k; i++) {
        ans = min(ans, f[tx][ty][i]); // 取最小值
    }
    return ans == INT_MAX ? -1 : ans;
}

int main() {
    cin >> n >> m >> k >> sx >> sy >> tx >> ty;
    for (int i = 1; i <= n; i++) { // 输入
        for (int j = 1; j <= m; j++) {
            char c;
            cin >> c;
            if (c == '.') mp[i][j] = 0;
            if (c == '#') mp[i][j] = -1;
            if (isdigit(c)) mp[i][j] = c - '0';
        }
    }
    printf("%d\n", bfs()); // 输出
    return 0;
}

代码片段见上,使用markdown转换器可得: