📅  最后修改于: 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转换器可得: