[POJ3744] Scout YYF I(概率, DP, 矩阵优化)

链接:https://cn.vjudge.net/problem/POJ-3744

一个人在一条线上走,线上有n个点有地雷,这个人每次有一定概率$p$走一步,也有$1-p$的概率跳两步。问这个人不踩地雷的概率是多少

我们根据题意能推出递推式:$f(i)$表示走到$i$处的概率为多少。$f(1)=1$,$f(i)=p×f(i-1)+(1-p)×f(i-2)$。

由于线很长,但是地雷很少。我们观察这个递推式是线性的,于是可以用矩阵快速幂优化,构造矩阵:
$$
\begin{pmatrix}f(n)\f(n-1)\end{pmatrix}=\begin{pmatrix}p&1-p\1&0\end{pmatrix}\begin{pmatrix}f(n-1)\f(n-2)\end{pmatrix}
$$
对于每一个雷的位置进行分段,计算到那个地方不踩中的概率,递推所有段走到雷处不中的概率,用乘法原理就能求出来结果了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
typedef struct Matrix {
double m[2][2];
}Matrix;

Matrix mul(Matrix a, Matrix b) {
Matrix ret;
memset(ret.m, 0, sizeof(ret.m));
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 2; j++) {
for(int k = 0; k < 2; k++) {
if(b.m[j][k] == 0) continue;
ret.m[i][k] += a.m[i][j] * b.m[j][k];
}
}
}
return ret;
}

Matrix M(Matrix a, int n) {
Matrix ret;
memset(ret.m, 0, sizeof(ret.m));
ret.m[0][0] = ret.m[1][1] = 1;
Matrix tmp = a;
while(n) {
if(n & 1) {
ret = mul(ret, tmp);
}
n >>= 1;
tmp = mul(tmp, tmp);
}
return ret;
}

const int maxn = 11;
int x[maxn];
int n;
double q;

signed main() {
// freopen("in", "r", stdin);
while(cin >> n >> q) {
for(int i = 0; i < n; i++) cin >> x[i];
sort(x, x+n);
Matrix ret;
double tot = 1.0;
Matrix p;
p.m[0][0] = q, p.m[0][1] = 1-q;
p.m[1][0] = 1, p.m[1][1] = 0;
ret = M(p, x[0]-1);
tot *= (1 - ret.m[0][0]);
for(int i = 1; i < n; i++) {
if(x[i] == x[i-1]) continue;
ret = M(p, x[i]-x[i-1]-1);
tot *= (1 - ret.m[0][0]);
}
printf("%.7f\n", tot);
}
return 0;
}