HDU4336 Card Collector DP求期望+状压

题目大意:要集齐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;
}

posted @ 2019-05-11 08:33  薄荷凉了夏  阅读(87)  评论(0编辑  收藏  举报