在某次考试的时候用过的办法,懒人必备……【笑哭】
一个非常显然的 dp,我们用 \(f[i][j]\) 表示第 \(i\) 行第 \(j\) 列的格子走到最后一排的期望步数转移即为
\(f[i][j] = \frac{f[i][j - 1] + f[i][j + 1] + f[i + 1][j] + f[i][j]}{4} + 1\)
略微的化一下简:
\(f[i][j] = \frac{f[i][j - 1] + f[i][j + 1] + f[i + 1][j] + 4}{3}\)
(当然,对于 j = 1 和 j = m 的情况是另外两个式子,但推导的方法是一样的)。这个式子使用高斯消元优化一下就能搞定,然而非常悲伤,像我这样的蒟蒻写高斯消元常常写挂,也不是很敢码。那怎么办办呢?人工大法好啊!
我们可以注意到行与行之间是不会相互转移的,我们可以认为当前行的 dp 数组为 f,下一行的 dp 数组为 g。那么对于 \(f[i][m]\) 而言,我们有
\(f[m] = \frac{f[m - 1] + g[m] + 3}{2}\)
也就是说,我们可以用 \(f[i - 1]\) 来表示出 \(f[i]\) 的值!
那么每从 \(f[j] -> f[j - 1]\),我们都可以得到一个递推式
\(f]j] = k * f[j - 1] + t\)
那么这个式子递归到尽头 f[1] 的时候
我们陡然发现式子中的未知数只剩下了一个 f[1]!
此时,只需不断回代就能得到 f[2 ... m]。
复杂度 \(O(n * m)\) ,非常优秀是吧~
#include <bits/stdc++.h> using namespace std; #define maxn 1050 #define db double int n, m, S, T; db f[maxn][maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) k = -1; c = getchar(); } while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar(); return x * k; } void DP(int p, int x, db k, db t) { if(!x) return; if(x == 1) { f[p][x] = (f[p + 1][x] + 3.0 + t) / (2.0 - k); return; } db k1 = 1 / (3.0 - k), t1 = (f[p + 1][x] + 4.0 + t) / (3.0 - k); DP(p, x - 1, k1, t1); f[p][x] = f[p][x - 1] * k1 + t1; } int main() { n = read(), m = read(), S = read(), T = read(); for(int i = n - 1; i >= 1; i --) { db k = 0.5, t = 0.5 * (3.0 + f[i + 1][m]); DP(i, m - 1, k, t); if(m == 1) f[i][m] = f[i + 1][m] + 2.0; else f[i][m] = k * f[i][m - 1] + t; } printf("%.10lf\n", f[S][T]); return 0; }
原文地址:https://www.cnblogs.com/twilight-sx/p/9909899.html
时间: 2024-10-10 10:34:08