【POJ3744】Scout YYF I-概率DP+矩阵加速优化
题目大意:一个侦察兵要通过一条“地雷之路”,他从第一个位置开始,有p的概率向前走一步,有(1-p)的概率向前走两步,地雷之路上有n颗地雷,位置在[1,1000000000]之间,问侦察兵顺利通过地雷之路(即走过第n个地雷且不踩到任何地雷)的概率。
做法:当i-1和i-2不是雷时,很容易得出递推方程:f[i]=f[i-1]*p+f[i-2]*(1-p),其中f[i]为安全走到第i个位置的概率。而安全通过第i颗雷的概率为f[coor[i]-1]*(i-p)(coor[i]为第i颗雷的坐标),而安全通过第i颗雷的概率就是安全走到第coor[i]+1个位置的概率,即f[coor[i]+1]=f[coor[i]-1]*(1-p),所以我们最终要求的就是f[coor[n]+1],而除了通过雷时需要特殊处理,其他的步骤都是相似的,因此可以将问题分成n段处理:1~coor[1],coor[1]+1~coor[2],......,coor[n-1]+1~coor[n]。由于数字很大,我们可以利用矩阵进行优化。设矩阵F[i]={f[i] \n f[i-1]},转移矩阵A={p 1-p \n 1 0},设两颗雷的坐标先后为s和t,可得:F[t-1]=A^(t-s-2)*F[s+1](想一想,为什么?),求出f[t-1],再计算f[t+1]=f[t-1]*(1-p),再进行下一个阶段的计算...最后算出f[coor[n]+1],即答案。注意要特判t-s=1的情况,防止进行快速幂时运行错误。而且,输入中地雷的坐标并不一定是按升序给出的,所以要先读完所有的坐标并排序过后才能进行处理(本人就被坑了......RE了一次......囧)。最后要注意的是,结果要精确到七位小数(听说网上很多大牛都被坑了,不解),不要弄错了。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,last,coor[11];
double p,start;
struct matrix
{
double s[3][3];
}M[32],E;
bool cmp(int a,int b)
{
return a<b;
}
void clear(matrix &A)
{
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
A.s[i][j]=0;
}
matrix mult(matrix A,matrix B)
{
matrix S;
clear(S);
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
for(int k=1;k<=2;k++)
S.s[i][j]+=A.s[i][k]*B.s[k][j];
return S;
}
matrix power(int x)
{
matrix S;
S=E;
int i=0;
while(x!=0)
{
if (x&1) S=mult(S,M[i]);
i++;x>>=1;
}
return S;
}
int main()
{
clear(E);for(int i=1;i<=2;i++) E.s[i][i]=1;
while(scanf("%d%lf",&n,&p)!=EOF)
{
for(int i=1;i<=n;i++) scanf("%d",&coor[i]);
sort(coor+1,coor+n+1,cmp);
M[0].s[1][1]=p;M[0].s[1][2]=1-p;
M[0].s[2][1]=1;M[0].s[2][2]=0;
for(int i=1;i<=31;i++) M[i]=mult(M[i-1],M[i-1]);
bool flag=1;
coor[0]=0;start=1;
for(int i=1;i<=n;i++)
{
if (coor[i]-coor[i-1]==1) {printf("0.0000000\n");flag=0;break;}
else start=start*power(coor[i]-coor[i-1]-2).s[1][1]*(1-p);
}
if (flag) printf("%.7lf\n",start);
}
return 0;
}