Card Collector
有n种卡,每一卡包有概率\(p_i\)出现卡i,只会出现一张以下的卡,询问卡包的期望数包含所有种类的卡,\(1 <= N <= 20\)。
解
法一:容斥原理
注意到数据范围很小,再此我们能很轻松地求出一种卡摸到的期望,而此时摸到的期望包含了其他的部分,于是考虑容斥。
不难有一种卡i,其摸到的期望数为\(\frac{1}{p_i}\),而对于摸到卡i,或者j的期望数不难得知,即把它们看成一个整体\(\frac{1}{p_i+p_j}\),于是我们可以以此为容斥依据,进行容斥即可。
参考代码:
#include <iostream>
#include <cstdio>
#define il inline
#define ri register
using namespace std;
double w[20];
int main(){
double sxr,ans;
int n,i,j,li,tot;
while(scanf("%d",&n)!=EOF){
for(i=0;i<n;++i)scanf("%lf",&w[i]);
li=1<<n,ans=0;
for(i=1;i<li;++i){
tot&=0,sxr=0;
for(j=0;j<n;++j)
if(i>>j&1)sxr+=w[j],++tot;
if(tot&1)ans+=1/sxr;
else ans-=1/sxr;
}printf("%.5lf\n",ans);
}
return 0;
}
法二:无后效性处理
注意到为期望题,而样本空间无限大,考虑无后效性做法,即以期望代无限状态,设\(E(x)\)表示已经摸到x对应二进制的卡种数的期望卡包数(如\(E(3)\)表示已经摸到第1中卡和第二种卡的卡包数期望),于是不难有转移
\[E(x)=1+\sum E(x)p_i+\sum E(y)p_j
\]
其中\(E(x)p_i\)表示摸到已经摸过的卡,概率分别为\(p_i\),y则是对应二进制位已经把没摸过的卡记录下来,概率分别为\(p_j\)。
例如,只有2种卡,设\(p_0\)为没有摸到任何卡的概率
\[E(0)=1+E(0)p_0+E(1)p_1+E(2)p_2
\]
\[E(1)=1+E(1)(p_0+p_1)+E(3)p_2
\]
\[E(2)=1+E(2)(p_0+p_2)+E(3)p_1
\]
\[E(3)=0
\]
于是对一般式变换一下,我们有
\[E(x)=\frac{1+\sum E(y)p_j}{(1-\sum p_i)}
\]
以此为递推依据记忆化搜索即可。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
using namespace std;
int n;double w[21];
double dp[1048576];
il double search(int);
int main(){
int i,li;
while(scanf("%d",&n)!=EOF){
w[n]=1;for(i=0;i<n;++i)scanf("%lf",&w[i]),w[n]-=w[i];
memset(dp,-127,sizeof(dp)),li=(1<<n)-1,dp[li]=0;
printf("%.5lf\n",search(0));
}return 0;
}
il double search(int er){
if(dp[er]>=0)return dp[er];
int i;double mom(1-w[n]),son(1);
for(i=0;i<n;++i)
if(er>>i&1)mom-=w[i];
else son+=search(er|1<<i)*w[i];
return dp[er]=son/mom;
}