POJ 3744 Scout YYF I:概率dp

题目链接:http://poj.org/problem?id=3744

题意:

  有n个地雷,位置为pos[i]。

  在每个位置,你向前走一步的概率为p,向前走两步的概率为1-p。

  你的初始位置为1。

  问你通过雷区的概率。

 

题解:

  表示状态:

    dp[i] = probability moving to i

    表示走到i的概率

 

  找出答案:

    ans = dp[last_mine+1]

    last_mine:最右边一颗雷的位置

 

  如何转移:

    dp[i] = dp[i-1] * p + dp[i-2] * (1-p)

    if(i is a mine) dp[i] = 0

    对于位置i,有可能是从i-1走来的,也有可能是从i-2走来的。

    加法原理。

 

  边界条件:

    dp[1] = 1

    初始位置为1。

 

  优化:

    矩阵快速幂。

    对于某一段没有地雷的区间,是满足矩阵快速幂的(只用到递推式,dp不改为0)。

    所以分段进行矩阵快速幂。

    

    将雷区划分为n段:

    1~pos[1], pos[1]+1~pos[2], pos[2]+1~pos[3]...

    

    容斥原理:P(通过某一段雷区) = 1 - P(踩到最右边的雷)

    乘法原理:P(通过总雷区) = ∏ P(通过每一段雷区)

 

    矩阵格式:

    

    初始矩阵:

    

    特殊矩阵:

    

 

AC Code:

  1 // state expression:
  2 // dp[i] = probability moving to i
  3 //
  4 // find the answer:
  5 // dp[last mine + 1]
  6 //
  7 // transferring:
  8 // dp[i] = dp[i-1] * p + dp[i-2] * (1-p)
  9 //
 10 // boundary:
 11 // dp[1] = 1
 12 // others = 0
 13 //
 14 // optimization:
 15 // quick pow for matrix
 16 // from x to y
 17 // res = start * special ^ (y-x)
 18 // dp[i] = res.val[0][0]
 19 #include <iostream>
 20 #include <stdio.h>
 21 #include <string.h>
 22 #include <algorithm>
 23 #define MAX_N 15
 24 #define MAX_L 5
 25 
 26 using namespace std;
 27 
 28 struct Mat
 29 {
 30     int n;
 31     int m;
 32     double val[MAX_L][MAX_L];
 33     Mat()
 34     {
 35         n=0;
 36         m=0;
 37         memset(val,0,sizeof(val));
 38     }
 39     void print_mat()
 40     {
 41         for(int i=0;i<n;i++)
 42         {
 43             for(int j=0;j<m;j++)
 44             {
 45                 cout<<val[i][j]<<" ";
 46             }
 47             cout<<endl;
 48         }
 49         cout<<endl;
 50     }
 51 };
 52 
 53 int n;
 54 int pos[MAX_N];
 55 double p;
 56 double ans;
 57 
 58 Mat make_unit(int k)
 59 {
 60     Mat mat;
 61     mat.n=k;
 62     mat.m=k;
 63     for(int i=0;i<k;i++)
 64     {
 65         mat.val[i][i]=1;
 66     }
 67     return mat;
 68 }
 69 
 70 Mat make_start()
 71 {
 72     Mat mat;
 73     mat.n=1;
 74     mat.m=2;
 75     mat.val[0][0]=0;
 76     mat.val[0][1]=1;
 77     return mat;
 78 }
 79 
 80 Mat make_special()
 81 {
 82     Mat mat;
 83     mat.n=2;
 84     mat.m=2;
 85     mat.val[0][0]=0;
 86     mat.val[0][1]=1-p;
 87     mat.val[1][0]=1;
 88     mat.val[1][1]=p;
 89     return mat;
 90 }
 91 
 92 Mat mul_mat(const Mat &a,const Mat &b)
 93 {
 94     Mat c;
 95     if(a.m!=b.n)
 96     {
 97         cout<<"Error: mul_mat"<<endl;
 98         return c;
 99     }
100     c.n=a.n;
101     c.m=b.m;
102     for(int i=0;i<a.n;i++)
103     {
104         for(int j=0;j<b.m;j++)
105         {
106             for(int k=0;k<a.m;k++)
107             {
108                 c.val[i][j]+=a.val[i][k]*b.val[k][j];
109             }
110         }
111     }
112     return c;
113 }
114 
115 Mat quick_pow_mat(Mat mat,long long k)
116 {
117     Mat ans;
118     if(mat.n!=mat.m)
119     {
120         cout<<"Error: quick_pow_mat"<<endl;
121         return ans;
122     }
123     ans=make_unit(mat.n);
124     while(k)
125     {
126         if(k&1)
127         {
128             ans=mul_mat(ans,mat);
129         }
130         mat=mul_mat(mat,mat);
131         k>>=1;
132     }
133     return ans;
134 }
135 
136 void read()
137 {
138     pos[0]=0;
139     for(int i=1;i<=n;i++)
140     {
141         cin>>pos[i];
142     }
143 }
144 
145 void solve()
146 {
147     sort(pos+1,pos+1+n);
148     Mat special=make_special();
149     ans=1;
150     for(int i=1;i<=n;i++)
151     {
152         Mat start=make_start();
153         Mat res=mul_mat(start,quick_pow_mat(special,pos[i]-pos[i-1]));
154         ans*=(1-res.val[0][0]);
155     }
156 }
157 
158 void print()
159 {
160     printf("%.7f\n",ans);
161 }
162 
163 int main()
164 {
165     while(cin>>n>>p)
166     {
167         read();
168         solve();
169         print();
170     }
171 }

 

转载于:https://www.cnblogs.com/Leohh/p/7465622.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值