嗯。。
这道题是对我而言意义非凡的一道题
我第一次打CSP-S见到的第一道题,考场上20pts。从2021年10.24CSP当天开始 在洛谷交了很多遍。
之后看题解,有很多种这个题的做法,于是学习了一下。思想都大同小异,在实现上有着区别。、
简单来说,就是STL全家桶。
(PS:这几种做法是笔者在不同时间写的不同做法,时间跨度很大,代码风格很大,请见谅)
分为两种停机坪,国内和国外,有 m 1 + m 2 m1 + m2 m1+m2个,给你 [ l , r ] [l,r] [l,r],为落地时间和起飞时间,不能降落的就等会再降落或者安排另外一个停机坪(如果有的话)。
给定 n , m 1 , m 2 n , m1 , m2 n,m1,m2和起飞落地时间,求最多能容纳多少飞机。
我们考虑一下,
这个起飞时间其实是一个线性关系,也就是在一条时间线上,你总能找到某个飞机的起飞时间和落地时间,
不过有了 n n n个停机坪的限制,我们就要考虑一些其他的东西。
设最后的答案为 t o t tot tot,那么每当一个飞机落地的时候,那么 t o t + + tot++ tot++,飞机起飞的时候, t o t − − tot-- tot−−。
※如果 t o t tot tot在 + + ++ ++操作之后超过停机坪的限制数量,那么它就不能降落,同时进行标记,以防后面操作时进行 − 1 −1 −1 操作。
具体做法:
(1)读入航班信息按照抵达时刻从小到大进行排序并对时刻进行离散化。
(2)枚举每个廊桥,并在上述的※条件下下对每个离散化后的时刻进行处理,依次得到最大航班数量。
(3)进行统计。
它的复杂度? O ( n m 1 + n m 2 ) O(nm_1 + nm_2) O(nm1+nm2),因为题面给的 m 1 + m 2 < = 5000 m1+m2<=5000 m1+m2<=5000,那其实在极端数据下近似为 O ( n 2 ) O(n^2) O(n2)的。
#include
using namespace std;
int n ;
int m1 , m2;
struct node{
int l , r;
bool operator<(const node &x)const{
return l < x.l;
}
}a[1000001] , b[1000001];
int len_a , len_b;
int Discrete_a[10000001];
int Discrete_b[10000001];
int pos_a[100000][2];// 0 航班序号、1 存储该时刻是否是飞机抵达
int pos_b[100000][2];
bool vis[1000000];
int ans ;
int solve(int pos[][2] , int len , int x){
int cnt_Bridge = 0 , Can_in = 0; //廊桥数量 可以进入的数量
memset(vis , 0 , sizeof(vis));
for(int i = 1 ; i <= len ; i ++){
if(pos[i][1]){
if(cnt_Bridge < x){ //约束条件
++cnt_Bridge;
++Can_in;
vis[pos[i][0]] = 1 ;
}
}else{
if(vis[pos[i][0]])
--cnt_Bridge;
}
}
return Can_in;
}
int main(){
cin >> n >> m1 >> m2;
for(int i = 1 ; i <= m1 ; i ++){
cin >> a[i].l >> a[i].r;
Discrete_a[++len_a] = a[i].l;
Discrete_a[++len_a] = a[i].r;
}
for(int i = 1 ; i <= m2 ; i ++){
cin >> b[i].l >> b[i].r;
Discrete_b[++len_b] = b[i].l;
Discrete_b[++len_b] = b[i].r;
}
sort(a + 1 , a + 1 + m1 );
sort(b + 1 , b + 1 + m2 );
sort(Discrete_a + 1 , Discrete_a + 1 + m1 * 2);
sort(Discrete_b + 1 , Discrete_b + 1 + m2 * 2);
// 全是排序
for(int i = 1 ; i <= m1 ; i ++){
a[i].l = lower_bound(Discrete_a + 1 , Discrete_a + len_a + 1 , a[i].l) - Discrete_a;
a[i].r = lower_bound(Discrete_a + 1 , Discrete_a + len_a + 1 , a[i].r) - Discrete_a;
pos_a[a[i].l][0] = pos_a[a[i].r][0] = i;
pos_a[a[i].l][1] = 1;
}
for(int i = 1 ; i <= m2 ; i ++){
b[i].l = lower_bound(Discrete_b + 1 , Discrete_b + len_b + 1 , b[i].l) - Discrete_b;
b[i].r = lower_bound(Discrete_b + 1 , Discrete_b + len_b + 1 , b[i].r) - Discrete_b;
pos_b[b[i].l][0] = pos_b[b[i].r][0] = i;
pos_b[b[i].l][1] = 1;
}
//全是离散化
for(int i = 0 ; i <= n ; i ++)
ans = max(ans , solve(pos_a , len_a , i) + solve(pos_b , len_b , n - i));
//最后的答案就是sol(posa,lena,i)+sol(posb,lenb,n-i)的最大值,我们下一步的优化从优化它开始。
cout << ans << endl;
return 0;
}
因为所有的飞机的起落都没有交集(这里指的是一条线段上,两个飞机在时间相交的情况不可能停在一个飞机坪上),所以航班其实可以表达成一个集合。
想要快速处理将飞机停靠在不同数量的停机坪上所容纳的飞机,就不要去重复处理同一件事。
这一点,可以用 前缀和 解决。
那我们就需要一个能够在 l o g n logn logn 内查找飞机的最晚离开时间的算法。
这一点,set最好不过。
#include
#define x first
#define y second
#define For(i, j, k) for (int i = j; i <= k; i ++ )
using namespace std;
const int N = 100010, INF = N;
typedef pair<int, int> PII;
int ans;
int n, m1, m2;
PII a[N];
PII b[N];
set<PII> A, B;
set<PII>::iterator id;
int num1[N], num2[N];
int add(set<PII> &S){
int res = 0, now = 0;
For(i,0,INF){
id = S.lower_bound({now, now}); // 寻找进入机场的飞机中最早的一个, 返回它所在的迭代器
if (id == S.end()) break;
res ++;
now = (*id).y;
S.erase(id);
}
return res;
}
int main()
{
scanf("%d %d %d", &n, &m1, &m2);
For(i, 1, m1) scanf("%d %d", &a[i].x, &a[i].y);
For(i, 1, m2) scanf("%d %d", &b[i].x, &b[i].y);
For(i, 1, m1) A.insert(a[i]);
For(i, 1, m2) B.insert(b[i]);
For(i, 1, n) num1[i] = num1[i - 1] + add(A);
For(i, 1, n) num2[i] = num2[i - 1] + add(B);
For(i, 0, n) ans = max(ans, num1[i] + num2[n - i]);
printf("%d", ans);
return 0;
}
很显然吧?
维护两个优先队列,一个是等待离开的航班队列,一个是空闲的廊桥队列。
如果当前有 i i i个停机坪可以放飞机,那么我们就把放不了飞机放在 i + 1 i+1 i+1处,这样的话可以构建出一个队列。
记录各停机坪停靠的航班数,同样的前缀和,然后枚举廊桥,然后再把两区的航班数加起来。
#include
using namespace std;
struct Range {
int x , y;
} a[1000001] , b[1000001];
int res1[10000001];
int res2[10000001];
int n ;
bool mycmp(const Range& a ,const Range& b){
return a.x < b.x;
}
void Work(Range* t , int m , int *res) {
priority_queue<pair<int , int> , vector<pair<int , int>> , greater<pair<int ,int >> > wait;
priority_queue<int , vector<int> , greater<int> > leisure;
for(int i = 1 ; i <= n ; i++) leisure.push(i);
for(int i = 1 ; i <= m ; i++) {
while(!wait.empty() && t[i].x >= wait.top().first) {
leisure.push(wait.top().second);
wait.pop();
}
if(leisure.empty()) continue;
int Top = leisure.top();
leisure.pop();
res[Top] ++;
wait.push(make_pair(t[i].y , Top));
}
for(int i = 1 ; i <= n ; i++) res[i] += res[i - 1];
}
int main() {
int m1 , m2;
cin >> n >> m1 >> m2;
for(int i = 1; i <= m1; i ++) cin >> a[i].x >> a[i].y;
for(int i = 1; i <= m2; i ++) cin >> b[i].x >> b[i].y;
sort(a + 1 , a + 1 + m1 , mycmp);
sort(b + 1 , b + 1 + m2 , mycmp);
Work(a , m1 , res1);
Work(b , m2 , res2);
int ans = 0;
for(int i = 0 ; i <= n ; i++) ans = max(ans , res1[i] + res2[n - i]) ;
cout << ans << endl;
return 0;
}