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 }

 


 

 

posted on 2012-09-13 16:06  奋斗青春  阅读(3205)  评论(0编辑  收藏  举报