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)\)

我们将上面方程中右边的未知项移到右边,然后将已知项作为常数项留在右边,写出系数矩阵

image-20250903190525444

但实际上这个线性方程组是比较特殊的,我们画个图就一目了然了

image-20250903184931999

看看这个矩阵,我们发现我们每次选 \(a_{i,i}\) 为主元即可,那么每次只需要对下面一行进行消元,并且只需要改动 \(i,i+1,m + 1\) 这三列即可,因为 \(i - 1\) 列已经被前面消成零

这里注意不能直接消上下两行,把整个矩阵消成对角矩阵,因为你消上面一行的时候 \(i + 1\) 列会被新赋值,就不是 0 了

image-20250904074039021

按照正确的方式,最后消完元后整个矩阵就会变成这样

image-20250904073715929

那么我们只需从最后一行慢慢往前回带即可求出最后的答案

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;
}
posted @ 2025-10-05 18:06  michaele  阅读(12)  评论(0)    收藏  举报