1.题目
问题描述
小C和他的领导小F计划一次飞行,但由于严格的航空管制,他们的飞机仅能按特定的路线飞行:飞机只能飞往当前机场的相邻机场或相同航空公司管理的机场。为了减少起飞次数,小C需要制定最优的飞行路线。机场由一个数组airports
标识,其中:
- 数组每个元素代表一个独特的机场,元素的值代表不同的航空公司。
airports[0]
为起点,airports[airports.length - 1]
为终点。- 假设小C当前在机场
i
,那么i - 1
和i + 1
(如果存在)代表邻近机场,飞机可以直接前往。 - 如果在机场
i
,且存在airports[i] == airports[j]
,则机场i
和机场j
同属一家航空公司,可直接飞往。
求最小起飞次数。
测试样例
样例1:
输入:airports = [10, 12, 13, 12, 14]
输出:
3
样例2:
输入:airports = [10, 11, 12, 13, 14]
输出:
4
样例3:
输入:airports = [7, 7, 7, 8, 9, 7]
输出:
1
2.思路
bfs
-
构造辅助数据结构:
- **航空公司映射:**使用一个哈希表(
unordered_map<int, vector<int>>
),将每个航空公司编号映射到该航空公司所管理的所有机场的下标。这样,当访问到某个机场时,可以直接获得同一家航空公司的所有其他机场下标,进行快速跳转。
- **航空公司映射:**使用一个哈希表(
-
利用广度优先搜索(BFS)寻找最短路径:
-
**初始状态:**从起点(下标 0)开始,将其加入队列,并标记为已访问。
-
**逐层扩展:**使用 BFS 的层级遍历,每一层对应一次跳转。每层处理时:
- 对当前队列中所有节点,依次检查:a. 是否已到达终点;b. 当前节点的左侧和右侧相邻机场(如果存在且未访问),将它们加入队列;c. 当前节点所属航空公司的所有其他机场(如果未访问),也加入队列。
- 为防止重复跳转,每次处理完某航空公司的所有机场后,可以清空该航空公司的列表。
-
**步数计数:**每处理完一层,将步数加 1。这样当首次遇到终点时,当前步数就是所需的最小跳转数。
-
-
返回结果:
当找到终点时,返回对应的跳转步数。
3.代码
先飞到最远的相同航空公司管理的机场,再飞相邻机场,这样的思路不一定能找到最优飞行路线
#include <iostream>
#include <vector>
int solution(std::vector<int> airports) {
// Please write your code here
int i = 0;
int n = airports.size();
int cnt = 0;
bool flag = false; //是否有相同机场的飞机
while (i != n - 1) {
for (int j = n - 1; j > i; j--) {
flag = false;
if (airports[i] == airports[j]) {
i = j;
cnt ++;
flag = true;
}
}
if (!flag ) {
i++;
cnt++;
}
}
return cnt;
}
int main() {
// You can add more test cases here
std::vector<int> airports1 = {10, 12, 13, 12, 14};
std::vector<int> airports2 = {10, 11, 12, 13, 14};
std::cout << (solution(airports1) == 3) << std::endl;
std::cout << (solution(airports2) == 4) << std::endl;
return 0;
}
bfs
#include <iostream>
#include <vector>
#include <unordered_map>
#include <queue>
using namespace std;
int solution(std::vector<int> airports) {
// Please write your code here
int n = airports.size();
if (n == 1) {
return 0; //只有一个机场,不需要起飞
}
// 1.初始化数据结构
unordered_map<int, vector<int>> airlineMap;
for (int i = 0; i < n; ++i) {
airlineMap[airports[i]].push_back(i);
}
queue<int> q;
vector<bool> visited(n, false);
// 2.BFS初始化
q.push(0);
visited[0] = true;
int steps = 0;
// 3.BFS遍历
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; ++i) {
int current = q.front();
q.pop();
// 检查是否到达目的地
if (current == n - 1) {
return steps;
}
// 检查相邻机场
if (current - 1 >= 0 && !visited[current - 1]) {
q.push(current - 1);
visited[current - 1] = true;
}
if (current + 1 < n && !visited[current + 1]) {
q.push(current + 1);
visited[current + 1] = true;
}
// 检查同一公司的机场
for (int next : airlineMap[airports[current]]) {
if (!visited[next]) {
q.push(next);
visited[next] = true;
}
}
// 清空当前航空公司机场列表,避免重复访问
airlineMap[airports[current]].clear();
}
steps++;
}
return steps;
}
int main() {
// You can add more test cases here
std::vector<int> airports1 = {10, 12, 13, 12, 14};
std::vector<int> airports2 = {10, 11, 12, 13, 14};
std::cout << (solution(airports1) == 3) << std::endl;
std::cout << (solution(airports2) == 4) << std::endl;
return 0;
}
在广度优先搜索(BFS)算法中,我们通常会使用一个外层的循环来逐层(按距离)遍历节点。具体来说:
-
层级遍历:
变量
steps
记录的是从起点到当前节点所经过的步数。在 BFS 中,每次从队列中取出所有当前层的节点(即距离起点相同步数的节点),然后再把它们的相邻节点加入队列,这样相邻节点就都在下一层。这个
for (int i = 0; i < size; ++i)
循环的作用就是遍历当前队列中所有节点(当前层的节点),确保我们在将steps
增加前,已经把同一层的所有节点都处理完毕。 -
计数步数:
这样设计可以确保每增加一层就代表多走了一步,进而准确计算到达目的地所需的最小步数。
简而言之,这个循环保证了我们按层级遍历节点,从而正确计算最短路径(或者说最少的步骤数)。
4.参考资料
阿D的最佳飞行路线探索 | 豆包MarsCode AI刷题阿D的最佳飞行路线探索,已知飞机只能飞向当前机场的前后相邻机场 - 掘金