題意:
小吃中有N種卡片,每種卡片 i 出現的概率為 pi ,一袋小吃有可能沒有卡片,但最多有一張.問集齊所有卡片需要購買小吃的袋數期望.
思路:
1.用狀壓dp,dp[ s ]表示在s狀態時,集齊所需要的袋數期望.
s = 11111表示N = 5時集齊的狀態,此時dp[ s ] = 0;
注意求期望的題,對於dp的定義一般都是從終態轉移到初態,也就是反著求.
因為"期望"是
確定事件的結果 * 該事件發生的概率 = 平均來看嘗試一次可以得到的結果,即期望
若是在s1狀態得到一張卡片轉移到了s2,那麼s2是一個確定的狀態,而在s1時則有多種可能性.由此可以理解反著求的合理性.
終態是初態的"去向",確是期望的"來源".
//281MS 8480K
#include<cstdio>
using namespace std;
const int MAXN=22;
double p[MAXN];
double dp[1<<MAXN];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
double tt=0;
for(int i=0;i<n;i++)
{
scanf("%lf",&p[i]);
tt+=p[i];
}
tt=1-tt;//tt就表示沒有卡片的概率了
dp[(1<<n)-1]=0;//全部收集到了就不需要再買了.求期望一般都是反著推.
for(int i=(1<<n)-2;i>=0;i--)//遍歷所有方案
{
double x=0,sum=1;///肯定要拿自己那一張卡片
for(int j=0;j<n;j++)
{
if((i&(1<<j)))x+=p[j];///如果此種卡片在i中已經存在,累加其概率
else sum+=p[j]*dp[i|(1<<j)];///若不存在,說明可以由此種情況轉化而來
///dp[i|(1<<j)]是"確定事件",p[j]是該確定事件發生的概率,相乘則表示期望.
}
dp[i]=sum/(1-tt-x);
}
printf("%.5lf\n",dp[0]);
}
return 0;
}
//250MS 8480K
#include<cstdio>
using namespace std;
const int MAXN = 22;
double p[MAXN],dp[1<<MAXN];
int main()
{
int N,m;
while(scanf("%d",&N)==1)
{
for(int i=0;i<N;i++)
scanf("%lf",p+i);
m = 1 << N ;
dp[m-1] = 0;
for(int i=m-2;i>=0;i--)
{
double sump = 0,sum = 1;
for(int j=0;j<N;j++)
{
if(!(i & (1<<j)))//位運算寫成了邏輯與...手殘
{
sump += p[j];//有用的概率
sum += p[j]*dp[i|(1<<j)];
}
}
dp[i] = sum / sump;
}
printf("%.5lf\n",dp[0]);///雖然樣例中輸出是保留了3位,但是題中描述是誤差1e-4的...所以...
}
}