投票(vote)
有一个很神奇也很重要的性质:
将 \(p_i\) 从小到大排好序,则存在一个最优方案,其选择的是一段前缀和后缀。
证明:
假设有一个选择的同学,他前后都存在未选的同学,考虑固定其他选中的同学时这个同学的概率的贡献,是一个一次函数 \(y=ax+b(1-x)=(a-b)x+b\),所以换成前后一定不劣。
直接Dp,枚举前缀后缀合并即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 2000 + 5;
const int eps = 1e-7;
int n, k;
double p[N], pre[N][N], suf[N][N];
void max(double &x, const double &y) {
if (x + eps < y)
x = y;
}
void init() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%lf", p + i);
sort(p + 1, p + n + 1);
}
void work() {
pre[0][0] = 1;
for (int i = 1; i <= n; i++) {
double p =::p[i];
for (int j = i; j >= 1; j--)
pre[i][j] = p * pre[i - 1][j - 1] + (1 - p) * pre[i - 1][j];
pre[i][0] = (1 - p) * pre[i - 1][0];
}
suf[n + 1][0] = 1;
for (int i = n; i >= 1; i--) {
double p =::p[i];
for (int j = n - i + 1; j >= 1; j--)
suf[i][j] = p * suf[i + 1][j - 1] + (1 - p) * suf[i + 1][j];
suf[i][0] = (1 - p) * suf[i + 1][0];
}
int m = k / 2;
double ans = 0;
for (int i = 0; i <= k; i++) {
int j = n + 1 + i - k;
double fm = 0;
for (int t = 0; t <= m; t++)
fm += pre[i][t] * suf[j][m - t];
max(ans, fm);
}
printf("%.7f\n", ans);
}
int main() {
freopen("vote.in", "r", stdin);
freopen("vote.out", "w", stdout);
init();
work();
return 0;
}