Toc
  1. CF167B Wizards and Huge Prize
Toc
0 results found
sher-wu
CF167B
2020/02/02 Code 多维DP

CF167B Wizards and Huge Prize

题意:
  你要去 \(n(n\leq200)\) 个地点比赛,顺序自定。获胜奖品有的是一个奖杯,有的是一个容量为 \(b_i\) 的装奖杯的书包。开始时你有一个容量为 \(k\) 的书包。每场比赛的获胜概率为 \(p_i\) 。问你至少赢 \(l\) 场且所有奖杯都被装入书包的概率为多少。

思路:
  其实是有一个相对简单的方法的。就是由于顺序自定,那肯定优先去奖品为书包的地方比赛。先排序把送书包的放在前面,然后 \(dp[i][j][k]\) 表示前 \(i\) 天赢了 \(j\) 把,剩余空间为 \(k\) 就ok了。再者 \(n\leq200\),故 \(k\) 只要计算 \(200\) 以内就好。   但是开始没想到这个三维DP的做法,而是用了二维DP。思路比较巧妙虽然WA了6发。 \(dp[i][j]\) 代表送包的比赛赢了 \(i\) 把,空余位置 \(j\) 个。 \(win[i]\) 代表不送包的比赛赢了 \(i\) 局的概率。   最关键的是两者怎么实现综合考虑。送包的比赛赢了 \(i\) 局以后,不送包的比赛至少需要赢 \(max(0,l-i)\) 局,但又最多赢 \(j\) 局。同时,\(win\) 的前缀和到底要用哪一段也要考虑清楚。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 220;
double dp[maxn][40220], win[40220], pre[40220], ans;
struct Node
{
    double pct;
    int cap;
    bool operator<(const Node &x)
    {
        return cap < x.cap;
    }
}p[maxn];
int n, l, k, nc, tot;

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*f;
}

inline void readin()
{
    n = read(), l = read(), k = read();
    tot += k;
    for (int i = 1; i <= n; ++i)
        p[i].pct = 1.0 * read() / 100;
    for (int i = 1; i <= n; ++i)
        p[i].cap = read(), tot += p[i].cap == -1 ? 0 : p[i].cap;
    sort(p + 1, p + 1 + n);
    nc = n;
    for (int i = 1; i <= n; ++i)
        if (p[i].cap != -1)
        {
            nc = i - 1;
            break;
        }
}

inline void run()
{
    dp[0][k] = 1;
    win[0] = 1;
    for (int i = 1; i <= nc; ++i)
        for (int j = i; j > 0; --j)
        {
            double tent = 1.0 * win[j - 1] * p[i].pct;
            win[j] += tent;
            win[j - 1] -= tent;
        }
    pre[0] = win[0];
    for (int i = 1; i <= tot; ++i)
        pre[i] = pre[i - 1] + win[i];
    for (int i = nc + 1; i <= n; ++i)
        for (int j = i; j > nc; --j)
            for (int q = tot; q >= p[i].cap; --q)
                if (dp[j - 1 - nc][q - p[i].cap])
                {
                    double tent = 1.0 * dp[j - 1 - nc][q - p[i].cap] * p[i].pct;
                    dp[j - nc][q] += tent;
                    dp[j - 1 - nc][q - p[i].cap] -= tent;
                }
    for (int i = 0; i <= n - nc; ++i) //最关键的地方
    {
        int tent = i >= l ? 0 : l - i;
        for (int j = tent; j <= tot; ++j)
            if (tent)
                ans += 1.0 * dp[i][j] * (pre[j] - pre[tent - 1]);
            else
                ans += 1.0 * dp[i][j] * pre[j];
    }
}

int main()
{
    readin();
    run();
    printf("%.12lf\n", ans);
    return 0;
}
打赏
支付宝
微信
p4
本文作者:sher-wu
版权声明:本文首发于sher-wu的博客,转载请注明出处!