题目大意:有一个 n ∗ m n * m n∗m的网格图,有一个机器人在 ( x , y ) (x,y) (x,y)位置,它可以等概率的选择向右,向左,向下或原地不动,如果它在第一列,那它不会有向左这个选择,如果它在最后一列,那么它不会有向右这个选择,问机器人走到最后一行的期望步数。
题解:期望DP,以每一层为一个阶段,对于第
i
i
i层,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示第 i 行,第 j 列的期望步数。
若
j
=
1
j = 1
j=1,
d
p
[
i
]
[
j
]
=
1
3
(
d
p
[
i
]
[
j
]
+
d
p
[
i
]
[
j
+
1
]
+
d
p
[
i
+
1
]
[
j
]
)
+
1
dp[i][j] = \frac{1}{3}(dp[i][j] + dp[i][j + 1] + dp[i + 1][j]) + 1
dp[i][j]=31(dp[i][j]+dp[i][j+1]+dp[i+1][j])+1
若
1
<
j
<
m
1 < j < m
1<j<m,
d
p
[
i
]
[
j
]
=
1
4
(
d
p
[
i
]
[
j
]
+
d
p
[
i
]
[
j
−
1
]
+
d
p
[
i
]
[
j
+
1
]
+
d
p
[
i
+
1
]
[
j
]
)
+
1
dp[i][j] = \frac{1}{4}(dp[i][j] + dp[i][j - 1] + dp[i][j + 1] + dp[i + 1][j]) + 1
dp[i][j]=41(dp[i][j]+dp[i][j−1]+dp[i][j+1]+dp[i+1][j])+1
若
j
=
m
j = m
j=m,
d
p
[
i
]
[
j
]
=
1
3
(
d
p
[
i
]
[
j
]
+
d
p
[
i
]
[
j
−
1
]
+
d
p
[
i
+
1
]
[
j
]
)
+
1
dp[i][j] = \frac{1}{3}(dp[i][j] + dp[i][j - 1] + dp[i + 1][j]) + 1
dp[i][j]=31(dp[i][j]+dp[i][j−1]+dp[i+1][j])+1
对于 层数 而言,转移方程没有后效性,状态不会成环,但对于列数而言,某一行的状态成环。
对于状态成环的DP,将要求的DP设为变量,转移式子就变成了一个多元一次方程。
以行为阶段,一行m列则有m个方程,求出这一行所有列的答案,依次递推求上去,每一行内部使用高斯消元, d p [ i + 1 ] [ j ] dp[i + 1][j] dp[i+1][j] 是已求过的行,视为常量。高斯消元本是 ( m 3 ) (m ^ 3) (m3)的复杂度,但这里因为转移只设计到3个变量,整个矩阵除主对角线,主对角线左右相邻的位置外其它位置均为0,可以在O(m)时间内完成消元求解,整体复杂度为O(n * m)。
注意边界问题,当 m = 1时,机器人只能呆在原地或向下走,构造矩阵的方式要改变。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<math.h>
using namespace std;
const double eps = 1e-11;
const int maxn = 1e3 + 10;
int n,m,x,y;
double dp[maxn][maxn];
double a[maxn][maxn],b[maxn];
int main() {
scanf("%d%d",&n,&m);
memset(dp,0,sizeof dp);
scanf("%d%d",&x,&y);
for(int i = n - 1; i >= 1; i--) {
for(int j = 2; j < m; j++) {
a[j][j] = -3.0 / 4;
a[j][j - 1] = 1.0 / 4;
a[j][j + 1] = 1.0 / 4;
b[j] = -1 - dp[i + 1][j] / 4;
}
if(m >= 2) {
a[m][m] = a[1][1] = -2.0 / 3,a[1][2] = a[m][m - 1] = 1.0 / 3;
b[1] = -1 - dp[i + 1][1] / 3;
b[m] = -1 - dp[i + 1][m] / 3;
}
else {
a[m][m] = a[1][1] = -1.0 / 2;
b[1] = -1 - dp[i + 1][1] / 2;
b[m] = -1 - dp[i + 1][m] / 2;
}
for(int j = 1; j < m; j++) {
if(fabs(a[j + 1][j]) < eps) continue;
double rate = a[j + 1][j] / a[j][j];
for(int k = 0; k < 2; k++)
a[j + 1][j + k] -= a[j][j + k] * rate;
b[j + 1] -= b[j] * rate;
}
for(int j = m; j > 1; j--) {
if(fabs(a[j - 1][j]) < eps) continue;
double rate = a[j - 1][j] / a[j][j];
a[j - 1][j] -= a[j][j] * rate;
b[j - 1] -= b[j] * rate;
}
for(int j = 1; j <= m; j++)
dp[i][j] = b[j] / a[j][j];
}
printf("%.10lf\n",dp[x][y]);
return 0;
}