[双指针]P4653 [CEOI2017]Sure Bet题解

同步发表于你谷博客

这是一篇时间复杂度为 \(O(n \log_2{n})\) 的双指针做法题解。

题意

有两组物品各 \(n\)\((1\leq n \leq 10^5)\) ,每组物品有一定的价值。你需要选择若干个物品,收益为两种物品中价值的 较少的那组物品的价值之和,减去选取的物品数量,使收益最大化。

思路

首先,显然的一点是,如果我们选择的话我们一定会选择这组中收益比较大一点的物品。

那么就好做了,因为如果我现在假如说令第一组取出的数的个数为 \(l\) ,第二组取出的个数为 \(r\) 。我现在个数为 \(l\) 就只可能选的是前 \(l\) 个,个数为 \(r\) 就只可能是选择的前 \(r\) 个。

那么我现在 最优情况 当第一组取 \(l\) 个时,两堆数字和假如说已经求出了,答案分别是 \(suma_{l}\)\(sumb_{r}\) 。我现在让第一组多选一个,即选择 \(l+1\) 个,那么这时候,第二组是不可能将之前的选择撤销的,毕竟这只会让开销变得更大。而不会使答案变得更大。

那么 \(l\)\(r\) 只需要分别在两堆上线性移动就 ok 了。

时间的瓶颈在于排序,去除排序时间复杂度是 \(O(n)\) 的。

#include <bits/stdc++.h>
#define double long double
using namespace std;
const int MAXN=100086;
int n;
double a[MAXN],b[MAXN];
bool cmp(const double &x,const double &y){
	return x>y;
}
double sum1,sum2,maxx;
bool judge(int l,int r){
//判断往后移动答案是否会变大,如果能变大就往后移,也是一种贪心的思想
	double res=min(sum1,sum2)-l-r+1; //没有移动之前的答案
	if(min(sum1,sum2+b[r])-l-r>res) return 1;
	else return 0;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i]>>b[i];
	sort(a+1,a+n+1,cmp); sort(b+1,b+n+1,cmp);
	int r=1;
	for(int l=1;l<=n;++l){
		sum1+=a[l];
		double res=0;
		while(judge(l,r)&&r<=n) sum2+=b[r++];
		maxx=max(maxx,min(sum1,sum2)-l-r+1);
		//每次移动结束记录一下答案。
	}
	cout<<fixed<<setprecision(4)<<maxx<<endl;
    return 0;
}
//Welcome back,Newhanser.

posted @ 2022-03-18 22:24  Mercury_City  阅读(132)  评论(0编辑  收藏  举报