NOI.AC-保镖【贪心,对顶堆】

正题

题目链接:http://noi.ac/contest/266/problem/795


题目大意

n n n个人第 i i i个巡逻一次 a i a_i ai秒,休息至少 b i b_i bi秒。
要求

  1. 任意时刻都有人巡逻
  2. 在一个人的两次相邻的巡逻直接不能有另一个人巡逻两次。

解题思路

对于性质二我们发现就是在 n n n个人里选择最少的人轮流巡逻使得任意时刻都有人巡逻。

我们考虑贪心,将人按照 b i b_i bi排序,然后枚举就好了,这样我们就确定了最大的 b i b_i bi,所以我们要求前面的最大的数的和使得它的和足够就好了。

但是这样我们会发现有许多问题,因为这个人两次巡逻的间隔是不计算自己的巡逻时间的,
也就是 s u m − a i > b i ⇒ s u m > b i + a i sum-a_i>b_i\Rightarrow sum>b_i+a_i sumai>bisum>bi+ai

所以我们改为按照 b i + a i b_i+a_i bi+ai排序就好了,然后对顶堆维护前若干个最大值使得他们的和满足 m a x { b i + a i } max\{b_i+a_i\} max{bi+ai}


c o d e code code

#include
#include
#include
#include
#define ll long long
using namespace std;
const ll N=5e5+10;
struct node{
	ll t,b;
}a[N];
ll n,sum,z,ans=2147483647;
priority_queue<ll> q1,q2;
bool cmp(node x,node y)
{return x.b==y.b?x.t<y.t:x.b<y.b;}
int main()
{
	//freopen("data.in","r",stdin);
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++){
		scanf("%lld%lld",&a[i].t,&a[i].b);
		a[i].b+=a[i].t;
	}
	sort(a+1,a+1+n,cmp);
	for(ll i=1;i<=n;i++){
		if(q1.empty()||a[i].t>-q1.top()){
			q1.push(-a[i].t);
			sum+=a[i].t;z++;
		}
		else q2.push(a[i].t);
		while(!q1.empty()&&sum>=a[i].b){
			sum+=q1.top();z--;
			q2.push(-q1.top());
			q1.pop();
		}
		while(!q2.empty()&&sum<a[i].b){
			sum+=q2.top();z++;
			q1.push(-q2.top());
			q2.pop();
		}
		if(sum>=a[i].b){
			ans=min(ans,z);
		}
	}
	if(ans==2147483647) printf("-1");
	else printf("%lld",ans);
}

你可能感兴趣的:(数据结构)