Jzoj P3386 守卫者的挑战___概率dp

题目大意:

擂台赛有 N 个挑战,一开始有一个容量为 K 的包包,第 i 项挑战有一个属性 a i ,如果 a i >= 0 ,表示这次挑战成功后可以再获得一个容量为 a i 的包包。
a i = 1 ,挑战成功后可以得到一个大小为 1 的地图残片。
队员们必须把获得的所有的地图残片都带走(没有得到的不用考虑,只需要完成所有 N 项挑战后背包容量足够容纳地图残片即可),才能拼出完整的地图。并且他们至少要挑战成功 L 次才能离开擂台。
给出每项挑战成功的概率,第 i 项挑战成功的概率为 p i
请求出他们获得的地图残片离开擂台的概率。

0 <= K <= 2000 0 <= N <= 200
1 <= a i <= 1000 0 <= L <= N 0 <= p i <= 100

分析:

因为不用考虑挑战的顺序,所以我们先考虑赢了不需要占用背包的情况,即优先赢了能获得背包容量的。
然后设 f [ i ] [ j ] [ k ] 为前 i 场比赛,赢了 j 场,背包容量为 k 的概率
因为情况再坏也就 n 个地图碎片需要带走,所以 m a x { k } = n
那么我们就可以不会炸内存了,然后对于 i 可以滚动一下
对于前 i 场而言,
i + 1 场赢了,则
f [ i + 1 ] [ j + 1 ] [ m i n ( n , k + a [ i + 1 ] ) ] + = f [ i ] [ j ] [ k ] p [ i + 1 ] 100
i + 1 场输了,则
f [ i + 1 ] [ j ] [ k ] + = f [ i ] [ j ] [ k ] ( 1 p [ i + 1 ] 100 )

最后的答案就是 i = L N j = 0 N f [ n ] [ i ] [ j ]

代码:

#include<algorithm>  
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define fo(i, j, k) for(i = j; i <= k; i++)
#define N 205

using namespace std;  

struct Node { double p; int v; }a[N];  
double f[2][N][N], ans;  
int p[N], n, l, m;  
int i, j, k;

bool cmp(Node aa, Node bb) {
    return aa.v > bb.v; 
}

int main() {
    scanf("%d %d %d", &n, &l, &m); 
    fo(i, 1, n) scanf("%lf", &a[i].p);
    fo(i, 1, n) scanf("%d", &a[i].v);
    sort(a+1, a+n+1, cmp);
    f[0][0][min(n,m)] = 1;   
    fo(i, 0, n-1) {
        int br = i%2;
        int dr = br^1;
        fo(j, 0, i+1) 
            fo(k, 0, n) f[dr][j][k] = 0;
        fo(j, 0, i)
            fo(k, 0, n) {  
                if (k+a[i+1].v >= 0) f[dr][j+1][min(n,k+a[i+1].v)] += f[br][j][k]*(a[i+1].p/100);  
                f[dr][j][k] += f[br][j][k]*((100-a[i+1].p)/100);  
            }
    }
    fo(i, l, n) 
        fo(j, 0, n) ans += f[n%2][i][j];  
    printf("%.6lf", ans);  
    return 0;
}  

猜你喜欢

转载自blog.csdn.net/gx_man_vip/article/details/81043967