题解:P1713 麦当劳叔叔的难题
题目传送门:
问题分析
这是一个关于在网格中寻找路径的问题,需要找到从左下角到右上角的最短路径和最长路径(每个格子只能经过一次),然后计算它们的时间差。
解决思路
最短路径:可以使用广度优先搜索(BFS)来找到,因为 BFS 保证找到的路径是最短的。 最长路径:这是一个 NP-Hard 问题,在本题中,我们可以通过分析发现,最长路径的长度等于网格总格子数减去障碍物数量再减 1。
C++ 代码实现
#include <iostream>
#include <vector>
#include <queue>
#include <utility>
#include <set>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
// 障碍物的集合,用于快速判断某个格子是否是障碍物
set<pair<int, int>> obstacles;
for (int i = 0; i < m; ++i) {
int x, y;
cin >> x >> y;
obstacles.insert({x, y});
}
// 起点和终点
pair<int, int> start = {1, 1};
pair<int, int> end = {n, n};
// 如果起点或终点是障碍物,直接输出0
if (obstacles.count(start) || obstacles.count(end)) {
cout << 0 << endl;
return 0;
}
// BFS计算最短路径
vector<vector<bool>> visited(n + 1, vector<bool>(n + 1, false));
queue<pair<pair<int, int>, int>> q;
q.push({start, 0});
visited[1][1] = true;
int min_time = -1;
// 四个方向:上、右、下、左
vector<pair<int, int>> directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
while (!q.empty()) {
auto [pos, time] = q.front();
q.pop();
int x = pos.first;
int y = pos.second;
// 如果到达终点,记录最短时间并退出循环
if (x == n && y == n) {
min_time = time;
break;
}
// 尝试四个方向
for (auto [dx, dy] : directions) {
int nx = x + dx;
int ny = y + dy;
// 检查是否越界、是否是障碍物、是否已访问
if (nx >= 1 && nx <= n && ny >= 1 && ny <= n &&
!obstacles.count({nx, ny}) && !visited[nx][ny]) {
visited[nx][ny] = true;
q.push({{nx, ny}, time + 1});
}
}
}
// 计算最长路径
int max_time = n * n - m - 1;
// 输出结果
cout << max_time - min_time << endl;
return 0;
}
代码解释
输入处理:读取网格大小 n 和障碍物数量 m,然后将每个障碍物的坐标存入集合中。
起点和终点检查:如果起点或终点是障碍物,直接输出 0。
BFS 计算最短路径: 使用队列进行 BFS,每个元素包含当前位置和到达该位置的时间。
使用 visited 数组记录已经访问过的格子,避免重复访问。
四个方向分别是上、右、下、左,按顺序尝试每个方向。
当到达终点时,记录最短时间并退出循环。
计算最长路径:最长路径的长度等于总格子数减去障碍物数量再减 1。
输出结果:输出最长路径和最短路径的时间差。
时间复杂度:BFS 的时间复杂度是 O (n²),因为每个格子最多被访问一次
空间复杂度:主要是 visited 数组和队列的空间,也是 O (n²)。
这个算法高效地解决了问题,通过 BFS 找到最短路径,并通过分析得出最长路径的长度。