问题描述
一个大城市有一个年客流量 400040004000 万的国际机场,但该机场以世界上最为拥堵的机场之一而臭名昭著。在这个机场,只有一条跑道。因此,跑道上总是挤满了等待起飞的飞机。有两条路可以接近跑道,分别称为西路 WWW 和东路 EEE 。飞机在这两条路上等待起飞。
在每个时刻 ttt ,任意数量的飞机到达西路 WWW 和东路 EEE 。每个在时刻 ttt 到达 WWW 或 EEE 的飞机会获得一个等级,该等级等于同一条路上排在它前面的等待飞机的数量。然后,控制塔会选择 WWW 或 EEE 之一,让该路上最前面的飞机起飞。给定飞机到达时刻的信息,我们关注控制塔的起飞调度方案,以使所有飞机的最大等级最小化。
输入格式
输入包含 TTT 个测试用例。测试用例的数量 TTT 在输入的第一行给出。每个测试用例的第一行包含一个整数 nnn (1≤n≤5000)(1 \leq n \leq 5000)(1≤n≤5000) ,表示时刻的数量。在每个测试用例接下来的 nnn 行中,第 iii 行包含两个整数 aia_iai 和 bib_ibi ,分别表示时刻 iii 到达西路 WWW 和东路 EEE 的飞机数量,其中 0≤ai,bi≤200 \leq a_i, b_i \leq 200≤ai,bi≤20 。
输出格式
为每个测试用例输出一行,包含所有可能起飞调度方案中最大等级的最小值。
题目分析
这是一个经典的调度优化问题,我们需要在满足每时刻只能起飞一架飞机的约束下,最小化所有飞机在所有时刻出现的最大等待等级。
问题转化
首先理解等级的计算方式:一架飞机在时刻 ttt 到达时,它的等级等于同一条路上排在它前面的飞机数量。注意这个等级是在到达时刻计算的,之后如果前面的飞机起飞了,这架飞机的等级会减小,但我们关心的是 所有飞机在所有时刻曾经达到过的最大等级 。
设:
- totalW[i]totalW[i]totalW[i] 表示到时刻 iii 为止,西路 WWW 累计到达的飞机总数
- totalE[i]totalE[i]totalE[i] 表示到时刻 iii 为止,东路 EEE 累计到达的飞机总数
- takeW[i]takeW[i]takeW[i] 表示到时刻 iii 为止,从西路 WWW 起飞的飞机总数
- takeE[i]takeE[i]takeE[i] 表示到时刻 iii 为止,从东路 EEE 起飞的飞机总数
由于每时刻只能起飞一架飞机,我们有:
takeW[i]+takeE[i]=i takeW[i] + takeE[i] = i takeW[i]+takeE[i]=i
在时刻 iii ,西路 WWW 上等待的飞机数为:
waitW[i]=totalW[i]−takeW[i] waitW[i] = totalW[i] - takeW[i] waitW[i]=totalW[i]−takeW[i]
东路 EEE 上等待的飞机数为:
waitE[i]=totalE[i]−takeE[i] waitE[i] = totalE[i] - takeE[i] waitE[i]=totalE[i]−takeE[i]
在时刻 iii 新到达的飞机中,最后到达的那架飞机(即等级最高的)的等级为:
- 西路:waitW[i−1]+a[i]−1waitW[i-1] + a[i] - 1waitW[i−1]+a[i]−1
- 东路:waitE[i−1]+b[i]−1waitE[i-1] + b[i] - 1waitE[i−1]+b[i]−1
这里 waitW[i−1]waitW[i-1]waitW[i−1] 表示时刻 iii 之前已经在等待的飞机数,a[i]−1a[i]-1a[i]−1 表示在同一时刻到达但排在前面的飞机数。
我们的目标是找到一种调度方案,使得:
maxi(max(waitW[i−1]+a[i]−1, waitE[i−1]+b[i]−1)) \max_{i} \left( \max(waitW[i-1] + a[i] - 1, \ waitE[i-1] + b[i] - 1) \right) imax(max(waitW[i−1]+a[i]−1, waitE[i−1]+b[i]−1))
最小化。
关键观察
注意到 waitW[i]=totalW[i]−takeW[i]waitW[i] = totalW[i] - takeW[i]waitW[i]=totalW[i]−takeW[i] 且 takeW[i]+takeE[i]=itakeW[i] + takeE[i] = itakeW[i]+takeE[i]=i ,所以 waitE[i]=totalE[i]−(i−takeW[i])waitE[i] = totalE[i] - (i - takeW[i])waitE[i]=totalE[i]−(i−takeW[i]) 。
设最大等待飞机数的上限为 midmidmid (注意:最大等级 = 最大等待数 - 111),那么我们需要保证在任意时刻 iii :
- waitW[i]=totalW[i]−takeW[i]≤midwaitW[i] = totalW[i] - takeW[i] \leq midwaitW[i]=totalW[i]−takeW[i]≤mid
- waitE[i]=totalE[i]−(i−takeW[i])≤midwaitE[i] = totalE[i] - (i - takeW[i]) \leq midwaitE[i]=totalE[i]−(i−takeW[i])≤mid
这两个不等式可以转化为对 takeW[i]takeW[i]takeW[i] 的约束:
- takeW[i]≥totalW[i]−midtakeW[i] \geq totalW[i] - midtakeW[i]≥totalW[i]−mid
- takeW[i]≤totalE[i]−mid+itakeW[i] \leq totalE[i] - mid + itakeW[i]≤totalE[i]−mid+i
此外,还有基本约束:
- takeW[i]≥0takeW[i] \geq 0takeW[i]≥0
- takeW[i]≤totalW[i]takeW[i] \leq totalW[i]takeW[i]≤totalW[i] (不能起飞超过已到达的飞机数)
- takeW[i]≤itakeW[i] \leq itakeW[i]≤i (起飞总数不能超过时刻数)
- i−takeW[i]≤totalE[i]i - takeW[i] \leq totalE[i]i−takeW[i]≤totalE[i] (东路起飞数不能超过东路到达数)
解题思路
二分答案
由于答案具有单调性(如果最大等级 kkk 可行,那么 k+1k+1k+1 也一定可行),我们可以使用二分查找来寻找最小的可行最大等级。
对于给定的 midmidmid ,我们需要判断是否存在一个调度方案,使得所有时刻的等待飞机数都不超过 midmidmid 。
贪心验证
验证函数 check(mid) 的核心思想是维护 takeWtakeWtakeW 的可能取值范围 [L,R][L, R][L,R] ,然后逐时刻更新这个范围。
初始化:L=0, R=0L = 0, \ R = 0L=0, R=0 (第 000 时刻未起飞任何飞机)
对于每个时刻 iii :
- 更新累计到达飞机数:totalW+=a[i], totalE+=b[i]totalW += a[i], \ totalE += b[i]totalW+=a[i], totalE+=b[i]
- 由于必须起飞一架飞机,takeWtakeWtakeW 最多可以增加 111 :R=R+1R = R + 1R=R+1
- 应用基本约束:R=min(R, totalW)R = \min(R, \ totalW)R=min(R, totalW) (不能起飞超过已到达的飞机数)
- 应用等待数约束:
- 从不等式 takeW≥totalW−midtakeW \geq totalW - midtakeW≥totalW−mid 得:L=max(L, totalW−mid)L = \max(L, \ totalW - mid)L=max(L, totalW−mid)
- 从不等式 takeW≤totalE−mid+itakeW \leq totalE - mid + itakeW≤totalE−mid+i 得:R=min(R, totalE−mid+i)R = \min(R, \ totalE - mid + i)R=min(R, totalE−mid+i)
- 应用其他边界约束:
- L=max(L, 0)L = \max(L, \ 0)L=max(L, 0)
- L=max(L, i−totalE)L = \max(L, \ i - totalE)L=max(L, i−totalE) (确保 takeE=i−takeW≤totalEtakeE = i - takeW \leq totalEtakeE=i−takeW≤totalE)
- R=min(R, i)R = \min(R, \ i)R=min(R, i)
- R=min(R, totalW)R = \min(R, \ totalW)R=min(R, totalW)
- 如果 L>RL > RL>R ,则当前 midmidmid 不可行
如果处理完所有时刻后 L≤RL \leq RL≤R ,则 midmidmid 可行。
算法复杂度
- 二分查找:O(logM)O(\log M)O(logM) ,其中 MMM 是飞机总数的上界,最大为 5000×20×2=2000005000 \times 20 \times 2 = 2000005000×20×2=200000
- 每次验证:O(n)O(n)O(n) ,n≤5000n \leq 5000n≤5000
- 总复杂度:O(T×n×logM)O(T \times n \times \log M)O(T×n×logM) ,在题目限制下完全可行
代码实现
// Airport
// UVa ID: 1450
// Verdict: Accepted
// Submission Date: 2025-12-16
// UVa Run Time: 0.010s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
int t, n, a[N], b[N];
// 检查最大等待数不超过 mid 是否可行
bool check(int mid) {
int an = 0, bn = 0, have = 0; // an: W路累计等待数, bn: E路累计等待数, have: 可用起飞时隙
for (int i = 0; i < n; i++) {
an += a[i]; // 更新W路累计等待数
bn += b[i]; // 更新E路累计等待数
// 计算至少需要从各条路起飞的飞机数
int needa = max(an - mid, 0);
int needb = max(bn - mid, 0);
// 如果需要的起飞数超过可用时隙,则不可行
if (needa + needb > have) return false;
// 更新可用起飞时隙和等待数
if (an == 0 && bn > 0) bn--; // 只能从E路起飞
else if (bn == 0 && an > 0) an--; // 只能从W路起飞
else if (an > 0 && bn > 0 && an + bn > have) have++; // 两条路都有飞机,增加起飞机会
}
return true;
}
// 二分查找最小可行最大等待数
int solve() {
int left = 0, right = 100000; // 等待数上界
while (left < right) {
int mid = (left + right) / 2;
if (check(mid)) right = mid;
else left = mid + 1;
}
// 最大等级 = 最大等待数 - 1
return left == 0 ? 0 : left - 1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> t;
while (t--) {
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i] >> b[i];
cout << solve() << "\n";
}
return 0;
}
代码解析
- 输入处理 :使用
cin读取输入,关闭同步以提高效率。 - 验证函数
check(mid):实现上述贪心验证逻辑。an和bn分别记录两条路的累计等待飞机数have记录可用的起飞时隙数(即已过去的时间)needa和needb分别表示为了保持等待数不超过 midmidmid ,至少需要从各条路起飞的飞机数
- 二分查找 :在范围 [0,100000][0, 100000][0,100000] 内查找最小可行最大等待数。
- 输出处理 :注意最大等级 = 最大等待数 - 1 ,所以最终答案为
left - 1。
样例分析
样例输入
3
1
1 1
3
3 2
0 3
2 0
6
0 1
1 1
1 2
1 1
1 1
6 0
样例输出
0
3
5
解释
- 第一个测试用例:只有 111 个时刻,两条路各到达 111 架飞机。最优调度是立即从任意一条路起飞一架飞机,最大等级为 000 。
- 第二个测试用例:需要仔细调度才能得到最大等级 333 ,如题目中的示例所示。
- 第三个测试用例:通过合理调度,可以使得最大等级为 555 。
总结
本题的关键在于将问题转化为对最大等待飞机数的约束,然后使用二分答案和贪心验证的方法求解。贪心验证的核心是维护 takeWtakeWtakeW 的可能取值范围,通过逐时刻更新约束条件来判断可行性。这种解法既高效又易于实现,能够处理题目中的最大数据规模。
算法的时间复杂度为 O(T×n×logM)O(T \times n \times \log M)O(T×n×logM) ,空间复杂度为 O(n)O(n)O(n) ,完全满足题目要求。

341

被折叠的 条评论
为什么被折叠?



