【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] f[i-1]},转移矩阵A={p 1-p 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
");flag=0;break;}
	  else start=start*power(coor[i]-coor[i-1]-2).s[1][1]*(1-p);
	}
	if (flag) printf("%.7lf
",start);
  }
  
  return 0;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793917.html