[GCJKickstart2018RoundA] B. Lucky Dip

题目链接:https://code.google.com/codejam/contest/9234486/dashboard#s=p1

有n个数,允许k次“取一个数再放回重新取”的操作,一个人希望拿到最大的那个数字。求取到数字最大的期望。

k=0时,期望就是均值,即E0=1nni=0vi

k=1时,每取到一个数可以有一次机会选择是否放回,但是期望拿到数字最大,因此要选取比k=0时期望大的数,E1=1nni=1max(vi,E0)

可以推广:Ek=1nni=1max(vi,Ek1)

假设计算Ek时,有t个数字使得Ek1vi大,那么就有nt个数使Ek1vi小,他们的对Ek的贡献分别是tEk1v<Ek1v。我们可以对v排序,前者的t可以二分得到,后者可以维护一个前缀和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
using namespace std;

using LL = long long;
const int maxn = 20020;
double v[maxn], sum[maxn];
int n, k;

int bs(int E) {
int lo = 1, hi = n;
int ret = -1;
while(lo <= hi) {
int mid = (lo + hi) >> 1;
if(v[mid] <= E) {
ret = mid;
lo = mid + 1;
}
else {
hi = mid - 1;
}
}
return ret;
}

signed main() {
freopen("in", "r", stdin);
freopen("out", "w", stdout);
int T, _ = 1;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &k);
memset(sum, 0, sizeof(sum));
double E = .0;
for(int i = 1; i <= n; i++) {
scanf("%lf", &v[i]);
}
sort(v+1, v+n+1);
for(int i = 1; i <= n; i++) sum[i] = sum[i-1] + v[i];
for(int i = 0; i <= k; i++) {
int t = bs(E);
E = (sum[n]-sum[t]) + t * E;
E /= n;
}
printf("Case #%d: %.8f\n", _++, E);
}
return 0;
}
Code 401: 未经授权的操作,请检查你的AppId和AppKey.
Powered By Valine
v1.5.2