HDU4336 Card Collector (概率dp+状压dp)
http://acm.hdu.edu.cn/showproblem.php?pid=4336
题意:有n种卡片,一个包里会包含至多一张卡片,第i种卡片在某个包中出现的次数为pi,问将所有种类的卡片集齐需要买的包的期望。
注意存在某个包中一张也没有。
分析:状态压缩有个挺显然提示,N<=20,这是在次落落的在提示你。
我们首先定义: dp[st] 表示 st 状态到目标状态 的期望是多少 ; st转化为二进制0表示当前状态没有这个bit的卡片 , 1表示当前状态有这个bit位的卡片;
然后有如下的转移:1. 没有卡片-> dp[st]->dp[st] 2.有存在的卡片;dp[st]->dp[st] 3.有没有存在的卡片 :dp[st]->dp[st|(1<<bit)]
所以我们可以得到如下公式:
则dp[i]=no*(dp[i]+1)+∑pp[j]*(dp[i]+1)+∑pp[k]*(dp[i|(1<<k)]+1).----(1)
no:表示没有卡片的概率,∑pp[j]表示第j种卡片已经存在,∑pp[k]表示第j种卡片当前还没有。
显然no+∑pp[j]+∑pp[k]=1,所以花间得dp[i]=1+(no+∑pp[j])*dp[i]+∑pp[k]*dp[i|(1<<k)],dp[1<<n-1]=0递推求出dp[0]即可。
需要注意的是:(1)公式是需要进行化简的 , 需要将dp[i] 提取出来
#include<bits/stdc++.h> using namespace std; double dp[(1<<20)+1],p[21]; int main() { int n; while(~scanf("%d",&n)) { memset(dp,0,sizeof(dp)); double no=0; for(int i=0 ; i<n ; i++) { scanf("%lf",&p[i]); no+=p[i]; } no=1-no; int all=(1<<n)-1; dp[all]=0; for(int st=all-1 ; st>=0 ; st--)///枚举的状态 { double pj=0,pk=0; for(int j=0 ; j<n ; j++) { if(!(st&(1<<j))) { pk+=p[j]*(dp[st|(1<<j)]); } else { pj+=p[j]; } } dp[st]=(1+pk)*1.0/(1-no-pj); } printf("%.4f\n",dp[0]); } }