概率DP小結


hdu 4089  Activation

http://acm.hdu.edu.cn/showproblem.php?pid=4089

去年北京現場賽的一道題,當時沒做過概率DP, 用DP怎么搞都不對,最近才把概率DP學了學終於把這道題A了。

有n個人排隊激活游戲賬號,每次會有四種情況發生。

1.激活失敗:  隊列保持不變,發生的概率為p1

2.連接失敗: 隊首重新排到隊尾,隊列長度不變,發生的概率為p2

3.激活成功: 隊首移出隊列,后面的元素都往前移一位,隊列長度減1,發生的概率為p3

4.服務器崩潰: 不再提供服務。

剛開始有n個人在排隊,Tomato在第m個位置,問最后服務器崩潰的時候Tomato在隊列的前k個位置 發生的概率。

用dp[i][j]記錄隊列里面有i個人,Tomato在第j個位置的概率,那最后dp[n][m]即為所求。

dp[i][1]=p1*dp[i][1]+dp[i][i]*p2+p4

dp[i][j]=p1*dp[i][j] + dp[i][j-1]*p2+dp[i-1][j-1]*p3+p4; (j<=k)

dp[i][j]=p1*dp[i][j] + dp[i][j-1]*p2+dp[i-1][j-1]*p3;(j>k)

p21=p2/(1-p1);

p31=p3/(1-p1);

p41=p4/(1-p1);

dp[i][1]=p21*dp[i][1]+p41;

dp[i][j]=p21*dp[i][j-1]+dp[i-1][j-1]*p31+p41;(j<=k)

dp[i][j]=p21*dp[i][j-1]+dp[i-1][j-1]*p31;(j>k)

因為dp[i-1][j-1]在遞推的時候可以解決

所以最后就轉換成:

a[n]=An*a[n-1]+Cn;

a[n-1]=An-1 * a[n-2]+Cn-1;

...

a[2]=A2*a[1]+C2;

a[1]=A1*a[n]+C1;

通過迭代可以首先求出a[n],之后就可以求出所有的。

View Code
 1 # include<stdio.h>
 2 # include<string.h>
 3 # include<stdlib.h>
 4 # include<math.h>
 5 # define N 2005
 6 # define eps 1e-10
 7 double dp[N][N],c[N];
 8 int main()
 9 {
10     int i,j,n,m,K;
11     double p1,p2,p3,p4;
12     double p21,p31,p41;
13     double ans,sum;
14     while(scanf("%d%d%d",&n,&m,&K)!=EOF)
15     {
16         scanf("%lf%lf%lf%lf",&p1,&p2,&p3,&p4);
17         if(fabs(p4)<eps) {puts("0.00000");continue;}  
18         p21=p2/(1-p1);
19         p31=p3/(1-p1);
20         p41=p4/(1-p1);
21         dp[1][1]=p4/(1-p1-p2);
22         for(i=2;i<=n;i++)
23         {
24             for(j=i;j>=2;j--)
25             {
26                 c[j]=p31*dp[i-1][j-1];
27                 if(j<=K) c[j]+=p41;
28             }
29             c[1]=p41;
30             ans=1;//系數
31             sum=0;//常數和
32             for(j=i;j>=1;j--)
33             {
34                 sum+=ans*c[j];
35                 ans*=p21;
36             }
37             dp[i][i]=sum/(1-ans);
38             dp[i][0]=dp[i][i];
39             for(j=1;j<i;j++)
40                 dp[i][j]=p21*dp[i][j-1]+c[j];
41         }
42         printf("%.5lf\n",dp[n][m]);
43     }
44     return 0;
45 }

hdu 4035 Maze

http://acm.hdu.edu.cn/showproblem.php?pid=4035

有n個房間,有n-1條路連接,剛開始一個人在第1個房間里,當在一個房間的時候會有三種情況發生:

1.被kill,然后重新在第1個房間復活 ,概率為K[i]

2.直接離開這n個房間,概率為E[i]

3.繼續訪問相鄰的房間,如果與該房間相鄰的房間數為m,到每一個房間的概率相等,均為(1-K[i]-E[i])/m;

問最后這人離開時所走步數的期望值。

假設用dp[i]表示 從第i個房間到最后離開所需要走的步數的期望值。

那么最后dp[1]即為所求。

可以把n個房間看成一個以1為根的樹。

若i為葉子節點: dp[i]=K[i]*dp[1]+E[i]*0+(1-K[i]-E[i])*(dp[F[i]]+1);   F[i]表示 i 的父親節點

不是葉子節點: dp[i]=K[i]*dp[1]+E[i]*0+(1-K[i]-E[i])/m * ( sum(dp[j]+1) + dp[F[i]]+1 );  j表示 i 的子節點,m表示與 i 相連的節點數

現在我們把dp[i]轉化成dp[i]=A[i]*dp[1] + B[i] *dp[F[i]] + C[i]的形式,

把dp[j]=A[j]*dp[1] + B[j] * dp[F[j]] + C[j]代入上式,然后進行比較,可以得出

    tt=(1-K[i]-E[i])/m;
    A[i]=(K[i]+tt*sum(A[j])) / (1-tt*sum(B[j]));
    B[i]=tt / (1-tt*sum(B[j]));
    C[i]=(1-K[i]-E[i]+tt*sum(C[j]))/(1-tt*sum(B[j])); 

   最后dp[1]=A[1]*dp[1]+B[1]*0+C[1];

dp[1]=C[1]/(1-A[1]);

View Code
 1 # include<stdio.h>
 2 # include<string.h>
 3 # include<stdlib.h>
 4 # include<math.h>
 5 # define N 10005
 6 # define eps 1e-10
 7 struct Edge{
 8     int from,to,next;
 9 }edge[2*N];
10 int tol,head[N];
11 double K[N],E[N];
12 double A[N],B[N],C[N];
13 void add(int a,int b)
14 {
15     edge[tol].from=a;edge[tol].to=b;edge[tol].next=head[a];head[a]=tol++;
16 }
17 void dfs(int u,int father)
18 {
19     int j,v,cnt=0;
20     double sumA,sumB,sumC,tt;
21     A[u]=B[u]=C[u]=0;
22     sumA=sumB=sumC=0.0;
23     for(j=head[u];j!=-1;j=edge[j].next)
24     {
25         v=edge[j].to;
26         if(v==father) continue;
27         cnt++;
28         dfs(v,u);
29         sumA+=A[v];
30         sumB+=B[v];
31         sumC+=C[v];
32     }
33     if(cnt==0) //葉子節點
34     {
35         A[u]=K[u];
36         B[u]=1-K[u]-E[u];
37         C[u]=1-K[u]-E[u];
38         return ;
39     }
40     if(u!=1) cnt++;
41     tt=(1-K[u]-E[u])/cnt;
42     A[u]=(K[u]+tt*sumA) / (1-tt*sumB);
43     B[u]=tt / (1-tt*sumB);
44     C[u]=(1-K[u]-E[u]+tt*sumC)/(1-tt*sumB);
45 }
46 int main()
47 {
48     int i,n,ncase,t;
49     int a,b;
50     scanf("%d",&ncase);
51     for(t=1;t<=ncase;t++)
52     {
53         scanf("%d",&n);
54         tol=0;
55         memset(head,-1,sizeof(head));
56         for(i=2;i<=n;i++)
57         {
58             scanf("%d%d",&a,&b);
59             add(a,b);
60             add(b,a);
61         }
62         for(i=1;i<=n;i++)
63         {
64             scanf("%lf%lf",&K[i],&E[i]);
65             K[i]/=100;
66             E[i]/=100;
67         }
68         dfs(1,0);
69         printf("Case %d: ",t);
70         if(fabs(1-A[1])<eps) printf("impossible\n");
71         else printf("%.6lf\n",C[1]/(1-A[1]));
72     }
73     return 0;
74 }

poj  2151 Check the difficulty of problems

http://poj.org/problem?id=2151

T個人做m道題,現已知每個人做出來每道題目的概率,問最后每個人都至少做出一道題,並且做題數最多的人所做的題數不小於n的概率。

首先可以求出每個人都至少做出一道題的概率,再求出每個人做的題目數都大於等於1並且小於n的概率。

我們用dp[i][j][k]來表示第i個人,前j道題目做出來k道的概率。

這樣上面兩個概率就很容易求了。

View Code
 1 # include<stdio.h>
 2 # include<string.h>
 3 # include<stdlib.h>
 4 # define Pr 32
 5 # define Te 1005
 6 double Probable[Te][Pr];
 7 double dp[Te][Pr][Pr];
 8 int main()
 9 {
10     int i,j,k,M,T,N;
11     double ans,ans1,sum;
12     while(scanf("%d%d%d",&M,&T,&N)!=EOF)
13     {
14         if(M==0 && N==0 && T==0) break;
15         for(i=1;i<=T;i++)
16             for(j=1;j<=M;j++)
17                 scanf("%lf",&Probable[i][j]);
18         memset(dp,0,sizeof(dp));
19         for(i=1;i<=T;i++)//dp[i][j][k],第i個隊的前j道題目做出來k道
20         {
21             dp[i][0][0]=1;
22             for(j=1;j<=M;j++)
23                 for(k=0;k<=j;k++)
24                 {
25                     if(k==0) dp[i][j][0]=dp[i][j-1][0]*(1-Probable[i][j]);
26                     else dp[i][j][k]=dp[i][j-1][k-1]*Probable[i][j]+dp[i][j-1][k]*(1-Probable[i][j]);
27                 }
28         }
29         ans=1;
30         for(i=1;i<=T;i++)
31         {
32             sum=0;
33             for(j=1;j<=M;j++)
34                 sum+=dp[i][M][j];
35             ans*=sum;
36         }
37         ans1=1;
38         for(i=1;i<=T;i++)
39         {
40             sum=0;
41             for(j=1;j<N;j++)
42                 sum+=dp[i][M][j];
43             ans1*=sum;
44         }
45         printf("%.3lf\n",ans-ans1);
46     }
47     return 0;
48 }

 

poj 3071 Football

http://poj.org/problem?id=3071

有2^n個球隊,然后兩兩進行淘汰賽,知道最后剩下一支隊伍,也就是冠軍。

現已知任何兩支球隊碰面時每支球隊獲勝的概率,問最后哪一支球隊奪冠的希望最大。

很明顯是一個概率dp的問題,需要注意的是每一輪淘汰賽,一個球隊有可能碰見哪些球隊。

這個可以先預處理出來。

View Code
 1 # include<stdio.h>
 2 # include<string.h>
 3 # include<stdlib.h>
 4 double dp[8][130],map[130][130];
 5 int a[10];
 6 struct node{
 7     int up,down;
 8 }s[8][130];
 9 int find(int i,int k)
10 {
11     int ans;
12     ans=(i-1)/a[k-1]+1;
13     if(ans%2) return ans+1;
14     return ans-1;
15 }
16 int main()
17 {
18     int i,j,k,n,index;
19     int ans;
20     double Max;
21     a[0]=1;
22     for(i=1;i<=7;i++)
23         a[i]=a[i-1]*2;
24     for(i=1;i<=7;i++)
25     {
26         ans=0;
27         k=0;
28         while(ans<128)
29         {
30             s[i][++k].up=ans+1;
31             s[i][k].down=s[i][k].up+a[i-1]-1;
32             ans=s[i][k].down;///第i輪淘汰賽在第k陣營的隊伍的上界與下界
33         }
34     }
35     while(scanf("%d",&n)!=EOF && n!=-1)
36     {
37         for(i=1;i<=a[n];i++)
38             for(j=1;j<=a[n];j++)
39                 scanf("%lf",&map[i][j]);
40         memset(dp,0,sizeof(dp));
41         for(i=1;i<=a[n];i++)
42             dp[0][i]=1;
43         for(k=1;k<=n;k++)
44         {
45             for(i=1;i<=a[n];i++)
46             {
47                 ans=find(i,k);//找第k輪與第i個隊伍所在的陣營對立的陣營
48                 for(j=s[k][ans].up;j<=s[k][ans].down;j++)
49                     dp[k][i]+=dp[k-1][i]*dp[k-1][j]*map[i][j];
50             }
51         }
52         Max=dp[n][1];
53         index=1;
54         for(i=2;i<=a[n];i++)
55             if(dp[n][i]>Max) {Max=dp[n][i];index=i;}
56         printf("%d\n",index);
57     }
58     return 0;
59 }

 

zoj 3551 Bloodsucker

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4530

剛開始有n-1個人,1個吸血鬼,然后以后每天這n個中的其中兩個會相遇,如果一個是吸血鬼,一個是人,那這個人有一定的概率p變成吸血鬼。

問着n個最后都變成吸血鬼所需天數的期望值。

用dp[i]來表示有i個吸血鬼時的期望值,dp[n]=0;

dp[1]即為所求。

dp[i]=p1*dp[i]+p2*dp[i+1]+1,(p1+p2=1)

View Code
 1 # include<stdio.h>
 2 # include<string.h>
 3 # include<stdlib.h>
 4 # define N 100005
 5 double dp[N];
 6 int main()
 7 {
 8     int j,ncase,n;
 9     double p,ans1,ans2,p2;
10     scanf("%d",&ncase);
11     while(ncase--)
12     {
13         scanf("%d%lf",&n,&p);
14         dp[n]=0; 
15         //dp[i],表示吸血鬼數量為i時,到目標還需要多少天
16         //dp[i]=p1*dp[i]+p2*dp[i+1]+1,(p1+p2=1)
17         for(j=n-1;j>=1;j--)
18         {
19             ans1=j;
20             ans1*=n-j;
21             ans2=n;
22             ans2*=n-1;
23             ans2/=2;
24             p2=ans1/ans2 * p;
25             dp[j]=dp[j+1]+1.0/p2;
26         }
27         printf("%.3lf\n",dp[1]);
28     }
29     return 0;
30 }

 

codeforces 148 D  Bag of mice

http://codeforces.com/problemset/problem/148/D

公主和龍輪流從bags里面取老鼠,誰先去到白鼠誰贏。

但是龍的身體比較龐大,會嚇着老鼠,每次當他取老鼠時,都會從袋子里面再跳出來一只老鼠。

求最后公主贏的概率多大。

直接DP就可以了。

View Code
 1 # include<stdio.h>
 2 # include<string.h>
 3 # include<stdlib.h>
 4 # define N 1005
 5 double dp[N][N][2];
 6 int w,b;
 7 int main()
 8 {
 9     int i,j;
10     while(scanf("%d%d",&w,&b)!=EOF)
11     {
12         memset(dp,0,sizeof(dp));
13         //dp[i][j][0],表示現在bags里面有i個白鼠,j個黑鼠,然后現在輪到公主了
14         //dp[i][j][1],表示現在bags里面有i個白鼠,j個黑鼠,然后現在輪到dragon了
15         for(i=0;i<=w;i++)
16             for(j=0;j<=b;j++)
17             {
18                 if(i!=0) dp[i][j][0]=1.0*i/(i+j); //輪到公主的時候公主直接取一個白鼠
19                 if(j!=0) dp[i][j][0]+=1.0*j/(i+j)*dp[i][j-1][1];//公主取的是一個黑鼠
20                 if(i>=1 && j>=1) dp[i][j][1]=1.0*j/(i+j)*(1.0*i/(i+j-1))*dp[i-1][j-1][0];//dragon取黑鼠,然后嚇跑一個白鼠
21                 if(j>=2) dp[i][j][1]+=1.0*j/(i+j)*(1.0*(j-1)/(i+j-1))*dp[i][j-2][0];//dragon取黑鼠,然后又嚇跑一個黑鼠
22             }
23             printf("%.9lf\n",dp[w][b][0]);
24     }
25     return 0;
26 }

 


 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM