22 ACwing 290 坏掉的机器人 题解
坏掉的机器人
题面
给定一张 N×M 的棋盘,有一个机器人处于 (x,y) 位置。
这个机器人可以进行很多轮行动,每次等概率地随机选择停在原地、向左移动一格、向右移动一格或向下移动一格。
当然机器人不能移出棋盘。
求机器人从起点走到最后一行的任意一个位置上,所需行动次数的数学期望值。
\(1 \le N, M \le 1000\)
题解
这道题不难想到设 \(f(i,j)\) 表示从 \((i,j)\) 走到最后一行任意位置的方案数,初始 \(f(n,j) = 0\) ,目标状态 \(f(x,y)\)
那么有转移
- \(j = 1\) ,\(f(i,1) = \frac 1 3 [f(i,1) + f(i,2) + f(i + 1, 1)] + 1\)
- \(j = m\) ,\(f(i,m) = \frac 1 3 [f(i, m) + f(i, m - 1) + f(i + 1, m)] + 1\)
- \(1 < j < m\) ,\(f(i,j) = \frac 1 4 [f(i, j) + f(i, j - 1) + f(i, j + 1) + f(i + 1, j)] + 1\)
观察转移,发现其不满足dp要满足的无后效性原则,所以我们不能简单的dp求解
发现对于某一行来说,如果我们已经能够求出 \(i + 1\) 行的答案,那么我们就有了 \(m\) 个方程,以及 \(m\) 个未知数,所以我们尝试用高斯消元求解答案
如果直接暴力高斯消元,发现时间复杂度是会爆炸的,每次高斯消元时间复杂度 \(O(m^3)\) ,共做 \(n\) 次高斯消元,总时间复杂度为 \(O(nm^3)\)
我们将上面方程中右边的未知项移到右边,然后将已知项作为常数项留在右边,写出系数矩阵
但实际上这个线性方程组是比较特殊的,我们画个图就一目了然了
看看这个矩阵,我们发现我们每次选 \(a_{i,i}\) 为主元即可,那么每次只需要对下面一行进行消元,并且只需要改动 \(i,i+1,m + 1\) 这三列即可,因为 \(i - 1\) 列已经被前面消成零
这里注意不能直接消上下两行,把整个矩阵消成对角矩阵,因为你消上面一行的时候 \(i + 1\) 列会被新赋值,就不是 0 了
按照正确的方式,最后消完元后整个矩阵就会变成这样
那么我们只需从最后一行慢慢往前回带即可求出最后的答案
code
看起来很难,写起来也不简单
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1e3 + 10;
int n, m, x, y;
double a[N][N], f[N][N];
void Guss () {
for (int i = 1; i <= m; i ++) {
for (int j = i + 1; j <= min (m, i + 1); j ++) {
double mul = a[j][i] / a[i][i];
for (int k = i; k <= i + 1; k ++) {
a[j][k] -= a[i][k] * mul;
}
a[j][m + 1] -= a[i][m + 1] * mul;
}
}
}
int main () {
cin >> n >> m >> x >> y;
if (m == 1) {
//特判,如果是这样的话,那么就只能原地不动或往下走
//有 f[i][1] = 1/2(f[i + 1][1] + f[i][1]) + 1;
//从而有 f[i][1] = f[i + 1][1] + 2;
printf ("%.4lf\n", (double)2 * (n - x));
} else {
for (int i = 1; i <= m; i ++) {
f[n][i] = 0;
}
for (int i = n - 1; i >= 1; i --) {
memset (a, 0, sizeof a);
a[1][1] = 2.0 / 3, a[1][2] = -1.0 / 3;
a[m][m] = 2.0 / 3, a[m][m - 1] = -1.0 / 3;
a[1][m + 1] = f[i + 1][1] / 3 + 1, a[m][m + 1] = f[i + 1][m] / 3 + 1;
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;
a[j][m + 1] = f[i + 1][j] / 4 + 1;
}
//高斯消元
Guss ();
//回带
f[i][m] = a[m][m + 1] / a[m][m];
for (int j = m - 1; j >= 1; j --) {
f[i][j] = (a[j][m + 1] - a[j][j + 1] * f[i][j + 1]) / a[j][j];
}
}
printf ("%.4lf\n", f[x][y]);
}
return 0;
}