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]);
    }

}
View Code

 

 

posted @ 2019-07-10 11:57  shuai_hui  阅读(112)  评论(0编辑  收藏  举报