[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\n",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\n",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\n",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\n",ans);

    }

    return 0;

}




 

 

你可能感兴趣的:(Collection)