关于此题[ABC382E] Expansion Packs 概率DP的一些总结
传送门
首先看到这道题,我们发现想要求收集K个卡牌的期望开包数,必须要先求出每个包开出0~n张卡各自的概率,于是预示着这道题将要进行两次概率DP。
首先我们求每个包开出0~n张卡各自的概率。这个很好求,我们假设f[i][j]表示前
当然可以通过倒序枚举的方式将f压到一维。我这里用的是滚动数组
然后就是求收集K个卡牌的期望开包数。我们假设
做此类期望,或者说概率DP的题目,我们要注意一个思维,即假设我们开了上帝视角,也就是假设我们目前已经知道了所有的
那么,我们现在假设
注意,这里我用p[k]来表示f[now][k],即我们第一遍DP求出来的概率,而不是题目中输入的概率!这里的1是因为我一定要打开面前的这个包,所以期望的开包数一定会加1。我们发现这个式子两端都有f[i],于是我们需要将f[i]提出来才能得到DP能使用的递推式,化简得到:
然后就可以解决这道题了。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
double p[5010],ans,sum;
double f[2][5010];
double ff[5010];
int main() {
cin >> n >> m;
for(int i = 1;i <= n;i++) cin >> p[i],p[i] /= 100;
int now = 0,last = 1;
f[now][0] = 1-p[1];
f[now][1] = p[1];
for(int i = 2;i <= n;i++) {
swap(now,last);
f[now][0] = f[last][0] * (1 - p[i]);
for(int j = 1;j <= i;j++) {
f[now][j] = p[i] * f[last][j-1] + (1 - p[i]) * f[last][j];
}
}
for(int i = 1;i <= m;i++) {
ff[i] = 1;
for(int j = 1;j <= min(n,i);j++) {
ff[i] += f[now][j] * ff[i - j];
}
ff[i] /= 1 - f[now][0];
}
printf("%.16lf",ff[m]);
return 0;
}