hdoj4336(容斥原理or概率DP)

题目大意:n种卡牌,每种卡牌被抽到的概率依次为P1,P2,,,Pn,抽一次最多抽到一张,最少0张。问抽满n种卡的次数的期望。

 

用容斥原理来做,先单独考虑,再减去重复的。比如有两种卡牌A,B,分别为p1,p2,抽到A牌的期望为1/p1,抽到B牌的期望为1/p2,然而抽A牌时会有对B牌的重复,抽B牌亦然。根据容斥原理,减去两者的并部分(这个并部分似乎在这里有些难以理解,对于概率论没学好的我。。。),这个并部分即抽到A或B的期望。即E=E1+E2-E(12)=1/p1 + 1/p2 - 1/(p1+p2)。

卡牌多了,奇加偶减。

AC代码:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<iostream>
 4 
 5 using namespace std; 
 6 
 7 double a[30];
 8 
 9 double solve(int x)
10 {    int n=(1<<x)-1;  //二进制枚举
11     double sum=0;
12     for(int j=1;j<=n;j++)
13     {
14         int flag=0;
15         double sum2=0;
16     for(int i=0;i<x;i++)
17         {
18             if(j&(1<<i))
19             {
20                 flag++;
21                 sum2+=a[i+1];
22             }
23         }
24         if(flag&1) sum+=1/sum2;
25         else sum-=1/sum2;
26     }
27     return sum;
28 }
29 int main()
30 {    int n;
31     while(~scanf("%d",&n))
32     {    for(int i=1;i<=n;i++)
33             scanf("%lf",&a[i]);
34         double sum=solve(n);    
35         printf("%.5f
",sum);
36     }
37     
38     
39     
40     return 0;
41 }

另一种做法:概率dp

像状压DP一样,1表示已经取到了这种卡,0表示还没。

先看个例子,假设有10种卡,每种卡片被抽到的概率均为0.1,dp分析,假如我们已经抽到了k种卡片,那么再抽到一种新的卡片(第k+1种)的概率即为(10-k)/10,再抽到这种卡片的期望次数即为10/(10-k)。

所以,dp[k+1] = dp[k] +  10/(10-k);

当然,本题概率并不均等,还可能抽一次,毛都没有。

上面的式子也是化简后的式子,因为还需考虑是哪k种卡片,第k+1种又是哪一种。

期望一般逆推:

dp[k]=sum{dp[k+1]*P(k->k+1)}+P(n-k)

设dp[(1<<n)-1]为0,这样得出的答案与原来的状态呈反序,好强啊。

代码:

 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <cstdio>
 5 using namespace std;
 6 const int maxn = 20;
 7 double dp[1<<maxn], p[maxn];
 8 int main()
 9 {
10     int n;
11     while(~scanf("%d", &n))
12     {
13         double cur = 0;
14         for(int i = 0; i < n; i++)
15         {
16             scanf("%lf", &p[i]);
17             cur += p[i];
18         }
19         
20         int last = (1 << n)-1;
21         dp[last]=0; 
22         for(int i = last-1; i >= 0; i--) 
23         {
24             double temp = 1-cur; //抽到空的概率 
25             double k=0;
26             for(int j = 0; j < n; j++)
27             {
28                 if((1<<j)&i) temp += p[j];
29                 else k += dp[i|(1 << j)]*p[j]; //
30             }
31             dp[i] = k/(1-temp)+1.0/(1-temp);
32 
33         }
34         printf("%.5f
", dp[0]);
35     }
36     return 0;
37 }
View Code2
原文地址:https://www.cnblogs.com/lnu161403214/p/8309545.html