分别模拟国内和国际航班使用最小可用廊桥编号,再枚举两区廊桥数量分配。
OJ: luogu
题目 ID: P7913
难度:普及+/提高
标签:模拟贪心优先队列
日期: 2026-07-06 08:46
题意
机场一共有
每个区内部遵循同一个规则:飞机按到达时间依次处理,如果此时有空闲廊桥,就停靠廊桥;否则只能停远机位。要求选择国内区廊桥数量,使停靠廊桥的飞机总数最大。
思路
最直接的想法是枚举国内区分到
// brute.cpp:小数据暴力解,枚举国内区分到多少廊桥,再直接模拟先到先得。
#include <bits/stdc++.h>
using namespace std;
const int MAXM = 105;
struct Flight {
int arrive;
int leave;
};
int n, m1, m2;
Flight domestic[MAXM], international_flight[MAXM];
bool cmp_flight(const Flight &a, const Flight &b) {
return a.arrive < b.arrive;
}
int simulate(Flight flights[], int m, int bridge_count) {
if (bridge_count == 0) {
return 0;
}
sort(flights + 1, flights + m + 1, cmp_flight);
priority_queue<int, vector<int>, greater<int> > free_bridge;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > busy;
for (int i = 1; i <= bridge_count; i++) {
free_bridge.push(i);
}
int answer = 0;
for (int i = 1; i <= m; i++) {
while (!busy.empty() && busy.top().first < flights[i].arrive) {
free_bridge.push(busy.top().second);
busy.pop();
}
if (!free_bridge.empty()) {
int id = free_bridge.top();
free_bridge.pop();
answer++;
busy.push(make_pair(flights[i].leave, id));
}
}
return answer;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m1 >> m2;
for (int i = 1; i <= m1; i++) {
cin >> domestic[i].arrive >> domestic[i].leave;
}
for (int i = 1; i <= m2; i++) {
cin >> international_flight[i].arrive >> international_flight[i].leave;
}
int answer = 0;
for (int domestic_bridge = 0; domestic_bridge <= n; domestic_bridge++) {
Flight d[MAXM], g[MAXM];
for (int i = 1; i <= m1; i++) {
d[i] = domestic[i];
}
for (int i = 1; i <= m2; i++) {
g[i] = international_flight[i];
}
int now = simulate(d, m1, domestic_bridge) + simulate(g, m2, n - domestic_bridge);
answer = max(answer, now);
}
cout << answer << '\n';
return 0;
}暴力的瓶颈在于:每枚举一个
关键观察是:对于同一个区域,我们可以一次模拟出“如果有
模拟时,始终把飞机分配给编号最小的空闲廊桥。设某架飞机被分到编号
于是对每个区域做一次扫描:
- 按到达时间排序所有航班;
- 用一个小根堆维护已经离开的飞机,把它们占用的廊桥放回空闲集合;
- 用另一个小根堆维护当前空闲廊桥编号;
- 每次取最小空闲编号分配,并统计这个编号被使用了几次;
- 最后做前缀和,得到
:给这个区域 个廊桥时能接多少航班。
国内区得到
max(A[i] + B[n - i])代码
// main.cpp:用最小可用廊桥编号模拟每个区域,统计分配 i 个廊桥能接多少航班。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
struct Flight {
int arrive;
int leave;
};
int n, m1, m2;
Flight domestic[MAXN], international_flight[MAXN];
int cnt_domestic[MAXN], cnt_international[MAXN];
int sum_domestic[MAXN], sum_international[MAXN];
bool cmp_flight(const Flight &a, const Flight &b) {
return a.arrive < b.arrive;
}
void calc(Flight flights[], int m, int result[]) {
sort(flights + 1, flights + m + 1, cmp_flight);
priority_queue<int, vector<int>, greater<int> > free_bridge;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > busy;
int limit = min(n, m);
for (int i = 1; i <= limit; i++) {
free_bridge.push(i);
}
for (int i = 1; i <= m; i++) {
while (!busy.empty() && busy.top().first < flights[i].arrive) {
free_bridge.push(busy.top().second);
busy.pop();
}
if (!free_bridge.empty()) {
int id = free_bridge.top();
free_bridge.pop();
result[id]++;
busy.push(make_pair(flights[i].leave, id));
}
}
for (int i = 1; i <= n; i++) {
result[i] += result[i - 1];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m1 >> m2;
for (int i = 1; i <= m1; i++) {
cin >> domestic[i].arrive >> domestic[i].leave;
}
for (int i = 1; i <= m2; i++) {
cin >> international_flight[i].arrive >> international_flight[i].leave;
}
calc(domestic, m1, sum_domestic);
calc(international_flight, m2, sum_international);
int ans = 0;
for (int i = 0; i <= n; i++) {
ans = max(ans, sum_domestic[i] + sum_international[n - i]);
}
cout << ans << '\n';
return 0;
}复杂度
设总航班数为
每个区域的航班排序复杂度为
总时间复杂度为
总结
本题不要把“分配国内/国际廊桥数量”和“区内先到先得模拟”绑在一起重复做。先分别算出每个区域在不同廊桥数量下的收益,再做一次枚举合并,问题就变成两个收益数组的拼接。
核心实现点是:区内模拟必须使用最小可用廊桥编号,这样每个编号的使用次数才可以做前缀和,表示不同廊桥数量下的答案。
