zoukankan      html  css  js  c++  java
  • 浅谈概率期望动态规划

    前言:自从去年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
    ",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
    ",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
    ",ans);
    41     }
    42     return 0;
    43 }
    View Code

    Summary:

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

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

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

  • 相关阅读:
    48. Rotate Image
    47. Permutations II
    46. Permutations
    45. Jump Game II
    44. Wildcard Matching
    43. Multiply Strings
    42. Trapping Rain Water
    41. First Missing Positive
    40. Combination Sum II
    39. Combination Sum
  • 原文地址:https://www.cnblogs.com/logic-yzf/p/7762993.html
Copyright © 2011-2022 走看看