贪心--2016cqround4火车运输

11 篇文章 0 订阅
P3827火车运输
时间限制 : - MS   空间限制 : 265536 KB  
评测说明 : 1000ms
问题描述

ByteLand火车站(编号0)每天都要发往全国各地N列客运火车,编号1 N。第i列火车的目的地是编号Si的火 车站。 对任意车站X,都与X+1车站有铁轨直接相连,因此火车站可以看成数轴上的整数点,第i列火车可以停靠区 间[0, Si]中的各个站点。每列火车装载乘客的最大容量为Ci。 有M个人需要乘坐火车。已知每个人的乘车区间为[Li, Ri],即是说,在Li上车,在Ri下车。 由于火车的容量限制,请你求出最多有多少人的乘车需求可以得到满足。



输入格式


第1行:2个整数N和M。
接下来N行,每行2个整数Si和Ci,表示第i辆车的目的站和容量。
接下来M行,每行2个整数Li和Ri,表示第i个乘客的乘车区间。
 



输出格式

第1行:1个整数,表示最多有多少乘客的乘车需求可以满足
 


样例输入
1 3 
10 2 
1 5 
3 7 
4 9



样例输出
2


提示
1 ≤ N,M ≤ 10^5
1 ≤ Si,Ci ≤ 10^9

1 ≤ Li ≤ Ri ≤ 10^9

分析:废话不多说,相信仔细看过题的同学们一定想了各种方法。直接说正解,这个题是一个经典的贪心模型!一辆容量为c的火车看成c辆容量为1的火车。那么每一辆火车毫无疑问不存在同一区间有两个或以上的人在上面,即一辆火车在一个位置上面最多只能有一个人。那么现在来说怎么贪心。

首先把火车按区间右端点由大到小排序,乘客双关键字排序(右端点由大到小优先)。显然排序后第一辆火车管不到的人就只有自杀了!然后,我们讨论每一个人,代码中now是记的当前这个人可以乘坐的火车数,cnt[i]是当前讨论过的人中左端点位于i的人数。原则:如果有空的火车,他当然可以坐,也可以选择和其他左端点在他的右端点右边的人一起坐,这也就是代码中now加的那一坨。now>0即这个人可以坐,那他就坐。那么堆是用来干嘛的?维护一些人的左端点,考虑这样一个问题,对于拼车的问题,因为是按右端点由大到小排序,先讨论的右端点一定大,如果一个人的区间是另一个人的子区间,对于拼车问题,这个人明显更优于另一个人!维护通过堆来实现!


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<vector>
using namespace std;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int n,m;
struct node{
	int l,r;
};
node pass[500005];
bool cmp1(node a,node b){
	if(a.r==b.r)return a.l>b.l;
	else return a.r>b.r;
}
struct node2{
	int x,v;
};
node2 train[500005];
bool cmp2(node2 a,node2 b){
	return a.x>b.x;
}
int tot=0;
int app[400005];
int cnt[500005];
priority_queue<int,vector<int>,greater<int> > q;
int ans=0;
int main(){
	int i,j,k;
	cin>>n>>m;
	for(i=1;i<=n;i++){
		_read(train[i].x);
		_read(train[i].v);
		app[++tot]=train[i].x;
	}
	for(i=1;i<=m;i++){
		_read(pass[i].l);
		_read(pass[i].r);
		app[++tot]=pass[i].l;
		app[++tot]=pass[i].r;
	}
	sort(app+1,app+1+tot);
	tot=unique(app+1,app+1+tot)-app-1;
	sort(train+1,train+1+n,cmp2);
	sort(pass+1,pass+1+m,cmp1);
	for(i=1;i<=n;i++){
		train[i].x=lower_bound(app+1,app+1+tot,train[i].x)-app;
	}
	for(i=1;i<=m;i++){
		pass[i].l=lower_bound(app+1,app+1+tot,pass[i].l)-app;
		pass[i].r=lower_bound(app+1,app+1+tot,pass[i].r)-app;
	}
	int now,last;
	i=1;j=1;
	last=pass[1].r;
	now=0;
	while(pass[i].r>train[1].x&&i<=m){
		i++;
	}
	for(;i<=m;i++){
		while(train[j].x>=pass[i].r&&j<=n){
			now+=train[j].v;
			j++;
		}
		while(last>pass[i].r){
			last--;
			now+=cnt[last];
		}
		if(now){
			now--;
			ans++;
			cnt[pass[i].l]++;
			q.push(pass[i].l);
		}
		else if(q.size()&&q.top()<pass[i].l){
			int temp=q.top();
			cnt[temp]--;
			cnt[pass[i].l]++;
			q.pop();
			q.push(pass[i].l);
		}
		
	}
	cout<<ans;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值