Random Walk(高斯消元求解方程组)
有一个
大小的格子。从
出发,每一步朝着上下左右
个格子中可以移动的格子等概率移动。另外有一些格子中有石头,因此无法移至这些格子。求第一次到达
格子的期望步数。题目假定至少存在一条从
出发到
的路径。
输入
10 10
..######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
.#######.#
....#.....
.####.####
....#.....
输出
1678.00000000
输入
10 10
..........
..........
..........
..........
..........
..........
..........
..........
..........
..........
输出
542.10052168
输入
3 10
.#...#...#
.#.#.#.#.#
...#...#..
输出
361.00000000
题解
设 表示从 出发,到终点的期望步数。我们考虑从 向上下左右 个方向都可以移动的情况。由于向 个方向的移动都是等概率的,因此可以在 和 之间建立起如下关系。
如果移动不是等概率的,只需要把 改成相应的数值就可以了。
如果存在不能移动的方向,我们也可以列出类似的式子。此外,当 时,我们有 。为了使方程有唯一解,我们令有石头的格子和无法到达终点的格子都有 。把得到的方程联立,就可以求解期望步数了。
代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <queue>
#include <map>
#define m_p make_pair
#define maxn 12
#define maxm 505
#define _for(i, a) for(int i = 0; i < (a); i++)
#define _rep(i, a, b) for(int i = (a); i <= (b); i++)
#define outval(a) cout<<#a<<": "<<a<<"\n"
using namespace std;
typedef long long LL;
const LL MAXN = 110;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
typedef vector< double > vec;
typedef vector< vec > mat;
const double eps = 1e-8;
char mp[maxn][maxn];
int n, m;
bool can_goal[maxn][maxn]; //能否到达终点
int dir[4][2] = { 0, 1, 1, 0, 0, -1, -1, 0 };
//Ax=b
vec gauss_jordan(const mat& A, const vec& b) {
int n = A.size();
mat B(n, vec(n + 1));
_for(i, n) {
_for(j, n) {
B[i][j] = A[i][j];
}
}
_for(i, n) B[i][n] = b[i];
_for(i, n) {
int pos = i;
_rep(j, i + 1, n - 1) {
if (fabs(B[j][i]) > fabs(B[pos][i])) pos = j;
}
if (pos != i) swap(B[pos], B[i]);
if (fabs(B[i][i]) < eps) return vec();
_rep(j, i + 1, n) B[i][j] /= B[i][i];
_for(j, n) {
if (i != j) {
_rep(k, i + 1, n) B[j][k] -= B[j][i] * B[i][k];
}
}
}
vec x(n);
_for(i, n) x[i] = B[i][n];
return x;
}
void init() {
memset(can_goal, 0, sizeof(can_goal));
}
double solve() {
mat A(n * m, vec(n * m, 0));
vec b(n * m, 0);
_for(i, n) {
_for(j, m) {
//如果是终点或者当前位置不能走就把期望等于0
if (i == n - 1 && j == m - 1 || !can_goal[i][j]) {
A[i * m + j][i * m + j] = 1;
continue;
}
//当前位置能走
int move = 0;
_for(k, 4) {
int x = i + dir[k][0];
int y = j + dir[k][1];
if (x >= 0 && x < n && y >= 0 && y < m && mp[x][y] == '.') {
A[i * m + j][x * m + y] = -1;
move++;
}
}
b[i * m + j] = A[i * m + j][i * m + j] = move;
}
}
b = gauss_jordan(A, b);
return b[0];
}
//找出所有能到达终点的点
void dfs(int x, int y) {
can_goal[x][y] = 1;
_for(k, 4) {
int ux = x + dir[k][0];
int uy = y + dir[k][1];
if (ux >= 0 && ux < n && uy >= 0 && uy < m && !can_goal[ux][uy] && mp[ux][uy] == '.') {
dfs(ux, uy);
}
}
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//freopen("in.txt", "r", stdin);
cin >> n >> m;
_for(i, n) cin >> mp[i];
init();
dfs(n - 1, m - 1);
printf("%.8f\n", solve());
return 0;
}
/*
*/