题意:要集齐n种卡片,现已知每打开一袋方便面得到每种卡片的概率Pi,Pi的和小于等于1,求集齐这n种卡片需要买的方便面的期望。
分析:
一、期望dp,这题要用二进制压缩,这是很显然的。
状态:dp[i]现在已经收集了i种卡片到达收集所有卡片的期望。这个状态能转移到的状态有:1).dp[i](下次打开的方便面得到的卡片是已经收集过的);2).dp[i|(1<<j)](下次得到的未收集过的卡片)
所以方程:dp[i]=Pi * dp[i] + Pj * dp[i|(1<<j)] + 1.0(不要忘了+1)其中pi是所有抽到已经收集过的卡片的概率和加上方便面里什么卡片都没有的概率,也就是不抽没收集过的卡片的反面。
实现:从后往前,从最后一个状态的前一个开始往前推,结果就输出dp[0]
二、容斥原理
1/pi是把第i种卡片单独看,收集到它的期望,但是把所有的卡片单独的期望加起来是不对的,因为他们的期望有交叉的地方,所以要减去1/(pi+pj),很好理解,就是两个集合重叠的部分,那么为什么是相加呢,分析一下这个重叠部分:应该是得A不得B,或者得B不得A,由于不可能既得A又得B,所以刚才那两种情况的概率就是Pa+Pb,以此类推。
dp代码:
#include<iostream> #include<cstdio> using namespace std; int n; double p[30],dp[1<<21]; int main() { while(cin>>n){ double q=1.0; for(int i=0;i<n;i++){ cin>>p[i]; q-=p[i]; } memset(dp,0,sizeof(dp)); for(int i=(1<<n)-2;i>=0;i--){ double pp=q; for(int j=0;j<n;j++){ if(i&(1<<j)) pp+=p[j]; else dp[i]+=p[j]*dp[i|(1<<j)]; } dp[i]+=1.0; dp[i]/=(1.0-pp); } printf("%f\n",dp[0]); } }容斥原理代码:
#include<iostream> #include<cstdio> using namespace std; int main() { int n; double a[30]; while(cin>>n){ double s=0; for(int i=0;i<n;i++) cin>>a[i]; for(int i=1;i<(1<<n);i++){ double sum=0; int tot=0; for(int j=0;j<n;j++){ if(i&(1<<j)){ tot++; sum+=a[j]; } } if(tot&1){ s+=1.0/sum; } else s-=1.0/sum; } printf("%lf\n",s); } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
HDU 4336 集齐方便面卡片的期望-期望dp 或 容斥原理 -(二进制压缩辅助)
原文地址:http://blog.csdn.net/ac_0_summer/article/details/47188763