[CEOI2017] Sure Bet
题目描述
现在有 $n$ 个A类灯泡和 $n$ 个B类灯泡,每个灯泡都有各自的权值。
我们将这些灯泡分为 $n$ 组,每组包含一个来自A类的灯泡和一个来自B类的灯泡。
你可以从中选取任意个灯泡,每选取一个灯泡需要花费 $1$ 的代价。
在你选取完之后,系统会随机在A类和B类中选择一个类型,并点亮那一类的所有灯泡。你选取的每个点亮的灯泡会给你带来等于它权值的收益。
现在请你合理选取灯泡,以最大化可能的最小收益。你只需要求出来这个收益即可。
输入格式
第一行一个正整数 $n$ ,表示灯泡的组数。
接下来 $n$ 行每行两个空格隔开的实数 $A_i,B_i$。分别表示属于这组的A灯泡和B灯泡的权值。输入的实数不会超过四位小数。
输出格式
输出最大化的最小可能收益,请输出到小数点后恰好四位。
样例 #1
样例输入 #1
4
1.4 3.7
1.2 2
1.6 1.4
1.9 1.5
样例输出 #1
0.5000
提示
样例解释
最优策略是选择第一组的B灯泡和第三组的A灯泡和第四组的A灯泡:
- 如果B类灯泡被点亮,收益是 $3.7-3=0.7$。
- 如果A类灯泡被点亮,收益是 $1.6+1.9-3=0.5$ 。
最小可能收益是 $0.5$。
数据范围
对于所有测试点,有 $1.0\le A_i,B_i\le 1000.0$,$0\le n\le 10^5$。
算法1
(贪心+双指针)
一开始没理解题意
求:最大的最小可能的收益
1.什么是最小可能的收益
各自灯泡的权值的和 - 选取灯泡的总数
如果选取 A 类灯泡 : sum_a - (n+m);
如果选取 B 类灯泡 : sum_b - (n+m);
所求可能的最小收益: min(sum_a - (n+m),sum_b - (n+m) );
2.何时可以得最大化的最小值
想获取最大的最小值把所有可能的组数当中取最大值;
因此把两个min取值变大,那么就把sum_a 和 sum_b 尽可能的取大一点
3.如何把最小值最大
挑 a 时,使a的权值尽可能大
挑 b 时,使b的权值尽可能大
贪心
首先对两个序列由大到小进行排序
当第一个序列的收益大于第二个序列的收益的时候,挑选第二个序列灯泡,使第二个序列的收益变大
最后
有可能某个序列已经遍历完之后,另一个序列还有剩的,那么继续挑选该序列,取最小的最大的
while(l<n){
l++;
ans = max(ans,min(sum_a[l]-l-r,sum_b[r]-l-r));
}
while(r<n){
r++;
ans = max(ans,min(sum_a[l]-l-r,sum_b[r]-l-r));
}
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n;
double a[N],b[N];
double sum_a[N],sum_b[N],ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf",&a[i],&b[i]);
}
sort(a+1,a+1+n,greater<double>());
sort(b+1,b+1+n,greater<double>());
for(int i=1;i<=n;i++){
sum_a[i] = sum_a[i-1] + a[i];
sum_b[i] = sum_b[i-1] + b[i];
}
int l=0,r=0;
while(l<n && r<n){
if(sum_a[l]<sum_b[r]) l++;
else r++;
ans = max(ans,min(sum_a[l]-l-r,sum_b[r]-l-r));
}
while(l<n){
l++;
ans = max(ans,min(sum_a[l]-l-r,sum_b[r]-l-r));
}
while(r<n){
r++;
ans = max(ans,min(sum_a[l]-l-r,sum_b[r]-l-r));
}
printf("%.4lf",ans);
return 0;
}
标签:Sure,sum,灯泡,选取,CEOI2017,权值,序列,收益,Bet From: https://www.cnblogs.com/ltphy-/p/18363073