POJ3744 Scout YYF 题解

题目链接

分析:

显然是一道概率DP

f i f_i fi表示安全到达第 i i i格的概率,在不考虑地雷的情况下,显然有:
f 1 = 1 , f 2 = p , f i = p f i − 1 + ( 1 − p ) f i − 2   ( i > = 3 ) f_1 = 1,f_2 = p,f_i = pf_{i - 1} + (1 - p)f_{i - 2}\,(i >= 3) f1=1,f2=p,fi=pfi1+(1p)fi2(i>=3)
那加上地雷呢?
注意到 n < = 10 n <= 10 n<=10,分开处理不就好了,可以分开计算每两个相邻地雷间的区域,遇到地雷就跳过,即:
f 地 雷 后 = ( 1 − p ) f 地 雷 前 f_{地雷后} = (1 - p)f_{地雷前} f=(1p)f
a i < = 1 0 8 a_i <= 10^8 ai<=108,会超时?


注意到这是一个二阶线性递推数列,用特征方程,解得: f n = f 1 ( ( p − 1 ) n − 1 ) p − 2 \sout{f_n = \frac{f_1((p - 1) ^ n - 1)}{p - 2}} fn=p2f1((p1)n1),快速幂求就好了~


可以用矩阵快速幂优化,令
F n = [ f n f n − 1 ] , G = [ p 1 − p 1 0 ] F_n = \left[\begin{matrix}f_n\\f_{n - 1}\end{matrix}\right],G =\left[\begin{matrix}p &1 - p \\ 1 & 0 \end{matrix}\right] Fn=[fnfn1]G=[p11p0]
则有
F n = F n − 1 G F_n = F_{n - 1}G Fn=Fn1G

F n = F 1 G n − 1 F_n = F_1G ^ {n - 1} Fn=F1Gn1
最后跑快速幂即可
注意特判两个地雷相邻的情况

Code:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 15;
int n,a[maxn];
double p,f;
struct matrix{
    double num[5][5];
}F;
int read(){
    int x = 0;
    char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + (c ^ 48),c = getchar();
    return x;
}
matrix mul(matrix x,matrix y){
    matrix d;
    for(int i = 1; i <= 2; i ++){
        for(int j = 1; j <= 2; j ++){
            d.num[i][j] = 0;
            for(int k = 1; k <= 2; k ++) d.num[i][j] += x.num[i][k] * y.num[k][j];
        }
    }
    return d;
}
matrix qpow(matrix x,int k){
    matrix t = x,d;
    d.num[1][1] = d.num[2][2] = 1,d.num[1][2] = d.num[2][1] = 0;
    while(k){
        if(k & 1) d = mul(d,t);
        k >>= 1,t = mul(t,t);
    }
    return d;
}
int main(){
    while(~scanf("%d%lf",&n,&p)){
        for(int i = 1; i <= n; i ++) a[i] = read();
        sort(a + 1,a + 1 + n);
        f = 1,F.num[1][1] = p,F.num[1][2] = 1 - p,F.num[2][1] = 1,F.num[2][2] = 0;
        for(int i = 1; i <= n; i ++){
            if(a[i] - a[i - 1] == 1){//如果相邻,肯定走不出去
                f = 0;
                break;
            }
            if(i != 1) f *= (1 - p);// 如果不是第一段,当前段第一格的概率为从上一段的最后一格跳两格的概率
            f *= qpow(F,a[i] - a[i - 1] - 2).num[1][1];//f变为当前段最后一格的概率
        }
        printf("%.7f\n",f * (1 - p));//最后一段右边还有一个地雷,做完这题才知道输出double要用%f,这题用%lf会WA!调了好久qwq
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值