题目大意:要集齐N张卡片,每包干脆面出现每种卡片的概率已知,问你集齐N张卡片所需要的方便面包数的数学期望(N<=20)。
solution:
由于N<=20,我们可以考虑状压,设dp[S]表示牌的状态为S时的需要的方便面包数的数学期望。
那么,对于每一个状态,考虑枚举每一张牌i(摸到了i),此时:
① 如果S中不含i,dp[S]+=(dp[S|(1<<i-1)]+1)*p[i]。
② 如果S中已经包含i,那么算到下面的情况中去。
但是注意到,上述情况是已经保证了摸到牌,但是其实可以没有摸到牌,结合②则dp[S]=(dp[S]+1)*P,其中P为摸不到任意一张牌或摸到一张已有的牌的概率,可以发现\(P+\sum{①中的p_i}=1\)
综上,得到\(dp[S]=(dp[S]+1) *P+\sum{_{!(S\&(1<<i-1))}(dp[S|(1<<i-1)]+1) *p[i]}\)。
把式子整理一下,得到: \(dp[s]=\frac{1+\sum{_{!(S\&(1<<i-1)}}dp[S|(1<<i-1)*p[i]]}{1-P}\)。
Code:
#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define LL long long
#define DB double
using namespace std;
const int N=1<<21;
DB p0,p[21],dp[N];
int main()
{
RG int n,i,j,all;
while(scanf("%d",&n)!=EOF) {
all=(1<<n)-1;
memset(dp,0,sizeof(dp));
for(i=1;i<=n;++i) scanf("%lf",&p[i]);
for(i=all-1;i>=0;--i) {
for(j=1,p0=0;j<=n;++j)
if(!(i&(1<<j-1))) dp[i]+=dp[i|(1<<j-1)]*p[j],p0+=p[j];
dp[i]=(dp[i]+1)/p0;
}
printf("%.4lf\n",dp[0]);
}
return 0;
}