Card Collector
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2421 Accepted Submission(s): 1140
Special Judge
As a smart boy, you notice that to win the award, you must buy much more snacks than it seems to be. To convince your friends not to waste money any more, you should find the expected number of snacks one should buy to collect a full suit of cards.
Note there is at most one card in a bag of snacks. And it is possible that there is nothing in the bag.
You will get accepted if the difference between your answer and the standard answer is no more that 10^-4.
题意:每包里面最多只有一张卡片(可能没有),要集齐n张不同的卡片(1<=n<=20)
问集齐n张卡片要买的包数期望。
算法:
1、 期望公式 :E=sum(xi*pi)。等于某一件事件的 状态 *它发生的概率。
在高中课本里就是列个分布列然后求期望。
我们用二进制枚举卡片表示各个状态。1表示已经集齐,0表示还没有。
从末态倒推到起始状态。
这里我们看每个状态是可能由哪个状态转移而来,乘以这个转移事件发生的概率
就是当前状态得到的期望了。
要注意的是这个转移事件发生的概率的理解:
它不是输入的那个概率,而是当前可以发生的转移中正在计算的转移所占的概率。
即如果当前状态能够从n种状态转移得来,则第i种发生的概率为pi/sum(p1....pn) ,其中sum(p1...pn)
p1...pn只是m个事件的子集(可以等于)
这就是为什么要除以能发生转移的概率和的原因。也是我高中学期望的时候老是出错的地方。
2、 容斥原理 :
容斥原理中奇加偶减
,比如:
如果被计数的事物有A、B两类,那么,A类B类元素个数总和= 属于A类元素个数+ 属于B类元素
个数—既是A类又是B类的元素个数。(A∪B = A+B - A∩B)
如果被计数的事物有A、B、C三类,那么,A类和B类和C类元素个数总和= A类元素个数+ B类
元素个数+C类元 素个数—既是A类又是B类的元素个数—既是A类又是C类的元素个数—既是B类
又是C类的元素个数+既是A类又是B类而且是C类的元素个数。(A∪B∪C = A+B+C - A∩B - B∩C - C∩A + A∩B∩C)
然后我们知道如果一件事发生的概率为pi,那么第一次发生这件事次数期望为1/pi。
同理,a和b这两件事发生的概率为p1,p2,则第一次发生某一件事发生的次数期望为1/p1+p2
如果一个事件发生的概率为p, 那么它第一次发生时的次数期望就是1/p
1: 1/p
2: (1-p)*p
3: (1-p)^2*p
......
n: (1-p)^(n-1)*p
以上求和,用错位相消的方法求出 E = 1/p
类似的,我们可以得出 如果两个事件发生的概率分别为p1,p2, 那么
第一次发生其中的某一件的次数期望就是1/(p1+p2)。
根据这个结论我们就可以用容斥原理来做这题了。
#include<stdio.h> double f[20]; int main() { int n,j,cnt,i; double sum,ans; while(~scanf("%d",&n)) { ans=0; for(i=1;i<=n;i++) scanf("%lf",&f[i]); for(i=1;i<1<<n;i++) { cnt=0; sum=0; for(j=1;j<=n;j++) { if(i&1<<(j-1)) { sum+=f[j]; cnt++; } } if(cnt%2==1) ans+=1.0/sum; else ans-=1.0/sum; } printf("%lf ",ans); } return 0; }
状态压缩:
#include<cstdio> #include<cstring> double dp[1<<20],p[22]; int main() { int n,tot; double tmp; while(scanf("%d",&n)!=EOF) { for(int i=0;i<n;i++) scanf("%lf",&p[i]); tot=(1<<n)-1; dp[tot]=0.0; for(int s=tot-1;s>=0;s--) { dp[s]=1; tmp=0.0; for(int j=0;j<n;j++) { if(s&(1<<j)) continue; dp[s]+=dp[s|(1<<j)]*p[j]; tmp+=p[j]; } dp[s]/=tmp; } printf("%lf ",dp[0]); } return 0; }