题意:
小吃中有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; }