[HDU 4336]Card Collection[状态压缩DP][概率DP][容斥原理]

题意:

小吃中有N种卡片,每种卡片 i 出现的概率为 pi ,一袋小吃有可能没有卡片,但最多有一张.问集齐所有卡片需要购买小吃的袋数期望.

思路:

1.用状压dp,dp[ s ]表示在s状态时,集齐所需要的袋数期望.

s = 11111表示N = 5时集齐的状态,此时dp[ s ] = 0;

注意求期望的题,对于dp的定义一般都是从终态转移到初态,也就是反着求.

因为"期望"是

确定事件的结果 * 该事件发生的概率 = 平均来看尝试一次可以得到的结果,即期望

若是在s1状态得到一张卡片转移到了s2,那么s2是一个确定的状态,而在s1时则有多种可能性.由此可以理解反着求的合理性.

终态是初态的"去向",确是期望的"来源".

//281MS	8480K
#include<cstdio>
using namespace std;
const int MAXN=22;
double p[MAXN];
double dp[1<<MAXN];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        double tt=0;
        for(int i=0;i<n;i++)
        {
            scanf("%lf",&p[i]);
            tt+=p[i];
        }
        tt=1-tt;//tt就表示没有卡片的概率了
        dp[(1<<n)-1]=0;//全部收集到了就不需要再买了.求期望一般都是反着推.
        for(int i=(1<<n)-2;i>=0;i--)//遍历所有方案
        {
            double x=0,sum=1;///肯定要拿自己那一张卡片
            for(int j=0;j<n;j++)
            {
                if((i&(1<<j)))x+=p[j];///如果此种卡片在i中已经存在,累加其概率
                else sum+=p[j]*dp[i|(1<<j)];///若不存在,说明可以由此种情况转化而来
///dp[i|(1<<j)]是"确定事件",p[j]是该确定事件发生的概率,相乘则表示期望.
            }
            dp[i]=sum/(1-tt-x);
        }
        printf("%.5lf
",dp[0]);

    }
    return 0;
}

自己敲一遍:

//250MS  8480K
#include<cstdio>
using namespace std;
const int MAXN = 22;
double p[MAXN],dp[1<<MAXN];
int main()
{
    int N,m;
    while(scanf("%d",&N)==1)
    {
        for(int i=0;i<N;i++)
            scanf("%lf",p+i);
        m = 1 << N ;
        dp[m-1] = 0;
        for(int i=m-2;i>=0;i--)
        {
            double sump = 0,sum = 1;
            for(int j=0;j<N;j++)
            {
                if(!(i & (1<<j)))//位运算写成了逻辑与...手残
                {
                    sump += p[j];//有用的概率
                    sum += p[j]*dp[i|(1<<j)];
                }
            }
            dp[i] = sum / sump;
        }
        printf("%.5lf
",dp[0]);///虽然样例中输出是保留了3位,但是题中描述是误差1e-4的...所以...
    }
}

2.容斥原理(先记下,稍后学习...)

//421MS	340K
#include<iostream>
#include<cstdio>
using namespace std;
double s;  int n,vis[25];
double a[25];
void dfs(int k,double sum,int cou,int j){
    if(cou==k){
        s+=1/sum;
        return ;
    }
    for(int i=j;i<=n;i++){
            sum+=a[i];
            cou++;
            dfs(k,sum,cou,i+1);
            cou--;
            sum-=a[i];
    }
    return ;
}
int main(){
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++){
            scanf("%lf",&a[i]);
        }
        double sum=0;
        for(int i=1;i<=n;i++)
            sum+=(1/a[i]);
        for(int i=2;i<=n;i++){
            s=0;
            dfs(i,0,0,1);
            if(i%2==0) sum+=(-1)*s;
            else sum+=s;
        }
        printf("%lf
",sum);
    }
}
/*
HDU 4336
容斥原理
位元素枚举
*/
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;

double p[22];
int main()
{
    int n;
    while(scanf("%d",&n)==1)
    {
        for(int i=0;i<n;i++)scanf("%lf",&p[i]);
        double ans=0;
        for(int i=1;i<(1<<n);i++)
        {
            int cnt=0;
            double sum=0;
            for(int j=0;j<n;j++)
              if(i&(1<<j))
              {
                  sum+=p[j];
                  cnt++;
              }
            if(cnt&1)ans+=1.0/sum;
            else ans-=1.0/sum;
        }
        printf("%.5lf
",ans);
    }
    return 0;
}


原文地址:https://www.cnblogs.com/bbsno1/p/3260450.html