http://acm.hdu.edu.cn/showproblem.php?pid=4336
1 0.1 2 0.1 0.4
10.000 10.500
题目大意:共有n张卡,每次一包方便面,可能有p[i]概率获得第i张卡,也有可能没有卡获得,求拿到全部n张卡需要买的方便面的期望?
感觉做了这么多概率dp,还是离熟悉比较远
合集里看到的,结果一眼就看到是用状态压缩做,然后状态都出来了,转移就没什么难度了...
设dp[i]表示当前取到了i的二进制中位的为1的卡时,离达到目标状态还需要购买方便面的期望,初始状态:dp[(1<<n)-1]=0;
则dp[i]可以转化为:
①:下一袋方便面没有卡,或j卡已有,即:(∑p[j]+pp)*(dp[i]+1);
②:下一袋方面面存在j卡,且当前没有,即:(∑p[j]*(dp[i|(1<<j)]+1);
则状态转移方程为:dp[i]=(∑p[j]+pp)*(dp[i]+1)+∑p[j]*(dp[i|(1<<j)]+1);
化简后得:dp[i]=(∑p[j]*dp[i|(1<<j)]+1)/(1-∑p[j]-pp);
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN=(1<<20)+5; int n,mx; double p[25],pp,tmp;//pp表示没有卡的概率 double dp[MAXN];//dp[i]表示当前取到了i的二进制中位的为1的卡时,离达到目标状态还需要购买方便面的期望 int main() { while(1==scanf("%d",&n)) { mx=1<<n; pp=1; for(int i=0;i<n;++i) { scanf("%lf",p+i); pp-=p[i]; } dp[mx-1]=0; for(int i=mx-2;i>=0;--i) { dp[i]=1; tmp=pp; for(int j=0;j<n;++j) { if((i&(1<<j))==0) {//由仅比i状态多1张卡的状态转移 dp[i]+=p[j]*dp[i|(1<<j)]; } else {//tmp表示再买一包时,里面没卡以及卡是已在i状态有时的概率和 tmp+=p[j]; } } dp[i]/=1-tmp; } printf("%.4lf\n",dp[0]); } return 0; }
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN=(1<<20)+5; int n,mx,cnt; double p[25],tmp,ans;//tmp表示某一状态下 int main() { while(1==scanf("%d",&n)) { mx=1<<n; ans=0; for(int i=0;i<n;++i) { scanf("%lf",p+i); } for(int i=1;i<mx;++i) { cnt=0; tmp=0; for(int j=0;j<n;++j) { if((i&(1<<j))!=0) {//统计达到i状态位为1的概率和 tmp+=p[j]; ++cnt; } } if((cnt&1)==0) {//偶数张卡 ans-=1.0/tmp; } else {//奇数张卡 ans+=1.0/tmp; } } printf("%.4lf\n",ans); } return 0; }