2021年的提高组第一题
学校模拟测试的时候居然想到了AC的代码(((bushi
题目意思
大体意思就是有n个廊桥,m1起国内航班,m2起国际航班,国内区和国际区是分开的,把n个廊桥分到两个区,飞机想要尽可能的停在廊桥处,而不停在远机处,每架飞机都有各自的起降时间,廊桥按到达顺序供给飞机,问怎么分廊桥可使得停在廊桥的飞机最多?
数据范围:1≤n≤105,m1,m2≥1,m1+m2≤105,ai<bi≤108
思路
因为题目中说廊桥是按飞机抵达的先后顺序分配的,我先想到了一个暴力思路:就是枚举分给国内的廊桥的个数(则分给国外的廊桥个数为n-i),再去枚举国内国外能有多少架飞机可以停在廊桥,取和的最大值,这个思路的复杂度在不优化的情况下大概是O(n^2(m1+m2)),这个复杂度一眼就知道会炸,大概只能过前20%的数据
接着,我否定了我上一个暴力思路后又想出了更好的一个暴力思路(别问我为啥老想暴力思路,因为正解也不是那么容易能思考出来的,这个离着我的AC思路已经很近了):我们不如先假设廊桥有无数个,去遍历国内(国外)的航班,设一个sum[i]表示第i个廊桥会停几架飞机,如果此时有空机位,飞机就停在空机位,sum[i]++,否则廊桥数tot++,sum[tot]=1,最后再用一个前缀和p,就可以求出当有i个廊桥的时候,可以停放多少飞机。
这个思路的时间复杂度是O(n*(m1+m2)),完全可以过掉前40%的点了。但是我的目标是正解,我们发现,在找空机位的时候特别浪费时间,是O(n)的复杂度,我们应该去考虑一种log级别的方法。
我先考虑的就是优先队列q1(太香了),把廊桥的编号和飞机起飞时间压入队列,按起飞时间由小到大排序,如果队首元素起飞时间大于当前遍历到的飞机,那么直接新建廊桥,否则将其停在队首廊桥上,压入队列。
注意!这里我犯了个非常致命的错误!!!
如果只是判定是否需要新建廊桥,是没有问题的,但是如果直接停在队首廊桥就会出现一个错误——可能会有编号更小的廊桥可以停,这样就会导致:假设这个飞机是可以停在1号廊桥,但由于3号廊桥的飞机起飞更早,导致该飞机停在了3号廊桥(所以会有啥后果呢?),那么我们在存储sum[i]的时候,就会把该飞机编入3号廊桥,也就是说至少要给国内分配3个廊桥时该飞机才可以停,那这显然不行。
正解
于是,我又增加了一个优先队列q2,用来从小到大存当前空机位廊桥的编号,如果有飞机的降落时间大于q1队首的起飞时间,那么我们直接把队首元素弹出,并把所对的廊桥编号压入q2,然后取q2队首编号的廊桥作为当前飞机的停机点。如果飞机的降落时间小于q1队首的起飞时间,先看看q2是否有空机位,如果没有再创建一个廊桥。时间复杂度是O((m1+m2)logn),可以过。
代码
考试时写的代码,难看繁琐,勿喷
#include <bits/stdc++.h>
using namespace std;
int n,m1,m2;
struct qu{
int l;
int r;
};
qu s1[101000],s2[101000];
struct node{
int id;
int ti;
bool operator < (const node &x) const{
return ti>x.ti;
}
};
priority_queue<node> q;
priority_queue<int,vector<int>,greater<int> > q2;
int sum1[101000],sum2[101000],p1[101000],p2[101000];
int tot1,tot2;
bool cmp(qu x,qu y){
return x.l<y.l;
}
int main (){
cin >> n >> m1 >> m2;
for(int i=1;i<=m1;i++)
cin >> s1[i].l >> s1[i].r;
for(int i=1;i<=m2;i++)
cin >> s2[i].l >> s2[i].r;
sort(s1+1,s1+m1+1,cmp);//先按降落时间从小到大排序
sort(s2+1,s2+m2+1,cmp);
for(int i=1;i<=m1;i++){
if(q.empty()||q.top().ti>s1[i].l){//如果当前所用着的廊桥已经没有可以停下的了
if(!q2.empty()) {//如果有空机位先停空机位
sum1[q2.top()]++;
q.push({q2.top(),s1[i].r});
q2.pop();
continue;
}
sum1[++tot1]++;//否则新建廊桥
q.push({tot1,s1[i].r});
}else{
while(!q.empty()&&q.top().ti<=s1[i].l){//先把所有符合要求的廊桥出队
node u=q.top();
q2.push(u.id);//压入q2
q.pop();
}
sum1[q2.top()]++;//sum存储
q.push({q2.top(),s1[i].r});
q2.pop();
}
}
while(!q.empty()) q.pop();//清空q1 q2
while(!q2.empty()) q2.pop();
for(int i=1;i<=n;i++) p1[i]=p1[i-1]+sum1[i];//前缀和求p,表示i个廊桥可停几个飞机
for(int i=1;i<=m2;i++){//国外同理
if(q.empty()||q.top().ti>s2[i].l){
if(!q2.empty()) {
sum2[q2.top()]++;
q.push({q2.top(),s2[i].r});
q2.pop();
continue;
}
sum2[++tot2]++;
q.push({tot2,s2[i].r});
}else{
while(!q.empty()&&q.top().ti<=s2[i].l){
node u=q.top();
q2.push(u.id);
q.pop();
}
sum2[q2.top()]++;
q.push({q2.top(),s2[i].r});
q2.pop();
}
}
for(int i=1;i<=n;i++) p2[i]=p2[i-1]+sum2[i];
int ans=0;
for(int i=0;i<=n;i++){//枚举廊桥的分配个数
ans=max(ans,p1[i]+p2[n-i]); //取最大值
}
cout << ans;
return 0;
}
总结
总体来说第一题还是有一定的思维的,这是本蒟蒻第一次写题解,若有什么问题请指出。
下课!
标签:飞机,q2,int,m1,2021,m2,廊桥,CSP From: https://www.cnblogs.com/free-fish/p/18446583