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++!!!

  • 相关阅读:
    python 函数嵌套
    python 函数对象
    python 函数参数
    python 连接MySQL报错及解决方案
    解决 No module named pip
    python 文件处理
    python
    python 元祖
    python 读取域名信息
    ubuntu 配置网卡,DNS, iptables
  • 原文地址:https://www.cnblogs.com/logic-yzf/p/7762993.html
Copyright © 2011-2022 走看看