CSP2021 S练习

T1 廊桥分配

解题思路:

wss:NOIP与CSP第一题基本都可以通过手♂玩发现一些性质与结论。那我们就先来手玩一下这个题的数据。

 我们通过手玩可以发现,首先有一个关键结论:如果给廊桥编上号,每当一架飞机到达后,如果强制让它进入编号最小的廊桥,这样不会影响进入廊桥飞机的集合,且当廊桥总数增加后,原本在哪个廊桥的飞机,仍旧会进入原来的廊桥。

 这个性质也很好理解:新加入廊桥可以视为原先的远机位的一部分,进入新加的这个廊桥的飞机可以视为在原来该进入远机位的飞机中贪心选择一轮。

 我们先忽略廊桥数量的限制来安排航班。维护一个空闲的廊桥队列,每到达一架航班,就给它安排编号最小的廊桥供其使用。

 现在加上廊桥数量的限制。容易发现刚才的廊桥分配方法直接就帮我们解决了廊桥限制的问题:如果当前有 n n n个廊桥可供使用,则分配到 n + 1 n+1 n+1号及以后的廊桥实质上就是分配到远机位了,不需要再做任何额外的处理。

 到这里做法就很清晰了:我们按照开头提到的分配方法来安排航班的停靠位置,记录各廊桥停靠的航班数,做一个前缀和,最后枚举分配给某个区的廊桥数,算出各情况下两区实际使用廊桥的航班数总和,即可解决本题。

代码:

#include<bits/stdc++.h>
#define in read()
#define MAXN int(1e5+50)
#define pii pair<int,int>
using namespace std;

struct node{
	int x,y;
	bool operator < (const node &rhs) const{
		return x<rhs.x;
	}
}a[MAXN],b[MAXN];
int n,m1,m2,arr1[MAXN],arr2[MAXN];

inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
	return x*f;
}

inline void prework(node t[],int m,int arr[]){
	priority_queue<int,vector<int>,greater<int> >q1;// 空闲廊桥队列
	priority_queue<pii,vector<pii>,greater<pii> >q2;// 等待离港航班队列 
	for(int i=1;i<=n;i++)q1.push(i);
	for(int i=1;i<=m;i++){
		while(!q2.empty() and t[i].x>=q2.top().first){
			q1.push(q2.top().second);
			q2.pop();
		}
		if(q1.empty())continue;
		int chs=q1.top();
		q1.pop();arr[chs]++;
		q2.push(make_pair(t[i].y,chs));
	}
	for(int i=1;i<=n;i++)arr[i]+=arr[i-1];
}

int main(){
	n=in,m1=in,m2=in;
	for(int i=1;i<=m1;i++)a[i].x=in,a[i].y=in;
	for(int i=1;i<=m2;i++)b[i].x=in,b[i].y=in;
	sort(a+1,a+m1+1),sort(b+1,b+m2+1);
	prework(a,m1,arr1),prework(b,m2,arr2);
	int ans=0;
	for(int i=0;i<=n;i++)
		ans=max(ans,arr1[i]+arr2[n-i]);
	cout<<ans<<'\n';
	return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值