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],之后就可以求出所有的。
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]);
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道的概率。
这样上面两个概率就很容易求了。
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的问题,需要注意的是每一轮淘汰赛,一个球队有可能碰见哪些球队。
这个可以先预处理出来。
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)
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就可以了。
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 }