浅谈概率期望动态规划

前言:自从去年noip有了换教室这道概率DP,

这以后各种OJ上的期望概率DP也越来越多,这里简单做个介绍

--------------------------------------------------------------------------------------------------

文章中涉及例题:hdu 4089,hdu 4405,hdu 4576,poj 2096,poj 3744 

[ 1 ] hdu 4405 机器人

要点:基础的概率DP+逆推

题意:

多组输入n,m,l,r。表示在一个环上有n个格子

接下来输入m个w表示连续的一段命令,每个w表示机器人沿顺时针或者逆时针方向前进w格,

已知机器人是从1号点出发的,输出最后机器人停在环上[l,r]区间的概率。n(1 ≤ n ≤ 200),m(0 ≤ m ≤ 1,000,000)

分析:我们可以很直接的列出转移方程

第一种:f[i]=f[i-w]*0.5+f[i+w]*0.5 

第二种:f[i-w]+=f[i]*0.5 , f[i+w]+=f[i]*0.5 (i-w和i+w位置根据模n来定)

因为大部分的概率期望DP都会运用到逆推的思想,因此我采用的是第一种转移方法

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 int n,m,x,y,to[101000];
 8 double f[100100];
 9 int main(){
10     while (scanf("%d%d",&n,&m)==2&&!(n==0&&m==0)){
11         memset(to,-1,sizeof(to));
12         for (int i=1;i<=m;++i)
13             scanf("%d%d",&x,&y),to[x]=y;
14         memset(f,0,sizeof(f));
15         for (int i=n-1;i>=0;--i)
16             if (to[i]!=-1) f[i]=f[to[i]];
17             else for (int j=1;j<=6;++j) f[i]+=(f[i+j]+1)/6.0;
18         printf("%.4f\n",f[0]);
19     }
20 }
View Code

 

[ 2 ] poj 2096 收集漏洞

要点:逆推+推式子

题意:

输入n,s表示这里存在n种漏洞和s个系统(0<n,s<=1000)。

工程师可以花费一天去找出一个漏洞——这个漏洞可以是以前出现过的种类,也可能是未曾出现过的种类

同时,这个漏洞出现在每个系统的概率相同。要求得出找到n种漏洞,并且在每个系统中均发现漏洞的期望天数。

分析:

这道题目我们应把方程设为四个部分:

(1)漏洞发现,系统发现漏洞;(2)漏洞发现,系统发现漏洞;

(3)漏洞发现,系统发现漏洞;(4)漏洞发现,系统发现漏洞;

分别对应了(1)f[i+1][j+1]  (2)f[i][j+1] (3)f[i+1][j] (4)f[i][j]

那么转移方程就可以轻松列出来了

如果我们设P1=(n-i)*(s-j)/n*s  P2=(n-i)*j/n*s  P3=i*(s-j)/n*s  P4=i*j/n*s

则 f[i][j] = (f[i][j]+1) * P+ (f[i][j+1]+1) * P+ (f[i+1][j]+1) * P+ (f[i+1][j+1]+1) * P1

因为我们发现左右都有f[i][j],那么我们将它化简

f[i][j] = (f[i][j+1] * P+ f[i+1][j] * P+ f[i+1][j+1] * P+ 1) / (1-P4)

这样就可以逆推了

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<cstdio>
 5 #include<algorithm>
 6 using namespace std;
 7 double f[1010][1010];
 8 int n,s;
 9 int main(){
10     while (~scanf("%d%d",&n,&s)){
11         f[n][s]=0;
12         for (int i=n;i>=0;--i)
13             for (int j=s;j>=0;--j) if (i!=n||j!=s)
14                 f[i][j]=(
15                     f[i+1][j]*(n-i)*j+
16                     f[i][j+1]*i*(s-j)+
17                     f[i+1][j+1]*(n-i)*(s-j)+n*s)
18                     /(n*s-i*j)*1.0;
19         printf("%.4f\n",f[0][0]);
20     }
21     return 0;
22 }
View Code

 

[ 3 ] poj 3744 YYF侦查员

要点:矩阵快速幂

题意:

输入n表示共有n个地雷(0<n<=10),并且输入每个地雷所在的位置ai (ai为不大于108的正整数)。

现在求从1号位置出发越过所有地雷的概率。

用两种行走方式:①走一步②走两步(不会踩爆中间那个雷)。这两个行为的概率分别为p和(1-p)。

分析:

先列出转移方程 f[i]=f[i-1]*p+f[i-2]*(1-p),但是这个方程的前提是他不死

那么这个方程只适用于没有地雷的情况,如果有地雷就只能走两步,跨过地雷了

你可能会以为这样就结束了,那你naifu了,数据范围是1e8,一步一步枚举早已T飞了

那我们想想如何优化这个过程

看到这个式子有没有联想到~~,斐波那契数列,那么是不是还可以用矩阵快速幂了呢

很明显是的嘛,好于是我们就可以构造出一个矩阵然后随意乱搞就行了

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 int a[100100],n;
 8 double p,ans;
 9 struct matr{
10     double f[3][3];
11     void init1(){
12         f[1][1]=p; f[1][2]=1-p;
13         f[2][1]=1; f[2][2]=0;
14         a[0]=0; ans=1;
15     }
16     void init2(){
17         f[1][1]=f[2][2]=1;
18         f[1][2]=f[2][1]=0;
19     }
20 }t;
21 matr operator *(matr a,matr b){
22     matr res;
23     for (int i=1;i<=2;++i) for (int j=1;j<=2;++j){ 
24         res.f[i][j]=0;
25         for (int k=1;k<=2;++k)
26             res.f[i][j]+=a.f[i][k]*b.f[k][j];
27     } return res;
28 }
29 void pow(matr a,int x){
30     matr res; res.init2();
31     while (x){if (x&1) res=res*a; a=a*a; x>>=1;}
32     ans*=(1-res.f[1][1]);
33 }
34 int main(){
35     while (scanf("%d%lf",&n,&p)==2){
36         for (int i=1;i<=n;++i) scanf("%d",&a[i]);
37         sort(a+1,a+n+1); t.init1();
38         for (int i=1;i<=n;++i) 
39             if (a[i]!=a[i-1]) pow(t,a[i]-a[i-1]-1);
40         printf("%.7f\n",ans);
41     }
42     return 0;
43 }
View Code

 

Summary:

总的来说概率期望DP只是一类问题,而他的实质是通过数学式子的推导,

再用一些简单的技巧来简化这个求解的过程,其中可能会有逆推,矩阵等等,

当然希望读者也可以在这基础上多加多加练习,祝各位NOIP RP++!!!

posted @ 2017-11-03 21:59  logiccc  阅读(1499)  评论(0编辑  收藏  举报