[双指针]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.