题意:
小吃中有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的...所以...
}
}
2.容斥原理(先记下,稍后学习...)
//421MS 340K
#include<iostream>
#include<cstdio>
using namespace std;
double s; int n,vis[25];
double a[25];
void dfs(int k,double sum,int cou,int j){
if(cou==k){
s+=1/sum;
return ;
}
for(int i=j;i<=n;i++){
sum+=a[i];
cou++;
dfs(k,sum,cou,i+1);
cou--;
sum-=a[i];
}
return ;
}
int main(){
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++){
scanf("%lf",&a[i]);
}
double sum=0;
for(int i=1;i<=n;i++)
sum+=(1/a[i]);
for(int i=2;i<=n;i++){
s=0;
dfs(i,0,0,1);
if(i%2==0) sum+=(-1)*s;
else sum+=s;
}
printf("%lf\n",sum);
}
}
/*
HDU 4336
容斥原理
位元素枚举
*/
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
double p[22];
int main()
{
int n;
while(scanf("%d",&n)==1)
{
for(int i=0;i<n;i++)scanf("%lf",&p[i]);
double ans=0;
for(int i=1;i<(1<<n);i++)
{
int cnt=0;
double sum=0;
for(int j=0;j<n;j++)
if(i&(1<<j))
{
sum+=p[j];
cnt++;
}
if(cnt&1)ans+=1.0/sum;
else ans-=1.0/sum;
}
printf("%.5lf\n",ans);
}
return 0;
}