题意:卡牌收集,有 n 张卡牌,每次拆开一包方便面有 pi 的 几率 得到第 i 张卡牌,保证 p1 + p2 + ... + pn <=1 , 所以方便面里可能存在没有牌的情况,求收集完 n 张卡牌,需要拆开的方便面包数的期望。
分析:首先想到这题应该是概率DP , 然后看看数据范围 n <= 20 , 很小,每张卡牌只有得到和没得到两个状态,所以可以想到用状压, 一个二进制位代表一种卡牌, 则设 dp[ k ] 代表当前收集的卡牌种类为 num( k ) [ k 的二进制 1 的位数 ] 的情况下收集完所有的卡牌需要拆掉的方便面数量的期望。我们所求即是 dp[ 0 ] , 易知 dp[ (1<<n)-1 ] =0;
当前为dp[ k ],下一包方便面一共分三种情况:
①:(1 + dp[ k ]) * pi //拆开有卡牌,且卡牌已经出现过
②:(1 + dp[ k | j ]) * pi //拆开有卡牌,但牌没有出现过
③:(1 + dp[ k ] ) * p(nothing) //拆开没有卡牌
综上可得转移方程 dp[ k ] = 1 + dp[ k ] * p(nothing) + dp[ k ] * (pA+pB+... ) + dp[ k | a ] * pa + dp[ k | b ] * pb + ...
移项整理得 :dp[ k ] = ( 1 + dp[ k | a ] * pa + dp[ k | b ] * pb + ... ) / (1-(pA + pB +...)-p(nothing) )
代码:
#include<cstdio>
#include<iostream>
using namespace std;
double dp[1<<20],p[20];
int main()
{
int n;
while(cin>>n)
{
double sum=1;
for(int i=0;i<n;i++)
{
cin>>p[i];
sum-=p[i];
}
dp[(1<<n)-1]=0;
for(int i=(1<<n)-2;i>-1;i--)
{
double s1=0,s2=0;
for(int j=0;j<n;j++)
if((i>>j)&1)
s1+=p[j];
else
s2+=dp[i|(1<<j)]*p[j];
dp[i]=(1+s2)/(1-(s1+sum));
}
printf("%.4f\n",dp[0]);
}
}