投票(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;
}
posted @ 2022-11-04 08:57  Southern_Way  阅读(192)  评论(0)    收藏  举报