Random Walk(高斯消元求解方程组)

Random Walk(高斯消元求解方程组)

有一个 N × M N \times M N×M 大小的格子。从 ( 0 , 0 ) (0,0) (0,0) 出发,每一步朝着上下左右 4 4 4 个格子中可以移动的格子等概率移动。另外有一些格子中有石头,因此无法移至这些格子。求第一次到达 ( N − 1 , M − 1 ) (N-1,M-1) (N1,M1) 格子的期望步数。题目假定至少存在一条从 ( 0 , 0 ) (0,0) (0,0) 出发到 ( N − 1 , M − 1 ) (N-1,M-1) (N1,M1) 的路径。
( 2 ≤ N , M ≤ 10 ) (2 \leq N,M \leq 10) (2N,M10)

输入

10 10
..######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
.#######.#
....#.....
.####.####
....#.....

输出

1678.00000000

输入

10 10
..........
..........
..........
..........
..........
..........
..........
..........
..........
..........

输出

542.10052168

输入

3 10
.#...#...#
.#.#.#.#.#
...#...#..

输出

361.00000000

题解

E ( x , y ) E(x,y) E(x,y) 表示从 ( x , y ) (x,y) (x,y) 出发,到终点的期望步数。我们考虑从 ( x , y ) (x,y) (x,y) 向上下左右 4 4 4 个方向都可以移动的情况。由于向 4 4 4 个方向的移动都是等概率的,因此可以在 E ( x , y ) E(x,y) E(x,y) E ( x + d x , y + d y ) ( ∣ d x + ∣ d y ∣ = 1 ) E(x+dx,y+dy)(|dx+|dy|=1) E(x+dx,y+dy)(dx+dy=1) 之间建立起如下关系。

E ( x , y ) = 1 4 E ( x − 1 , y ) + 1 4 E ( x + 1 , y ) + 1 4 E ( x , y − 1 ) + 1 4 E ( x , y + 1 ) + 1 E(x,y)= \frac{1}{4} E(x-1,y)+\frac{1}{4}E(x+1,y)+\frac{1}{4}E(x,y-1)+\frac{1}{4}E(x,y+1)+1 E(x,y)=41E(x1,y)+41E(x+1,y)+41E(x,y1)+41E(x,y+1)+1

如果移动不是等概率的,只需要把 1 4 \frac{1}{4} 41 改成相应的数值就可以了。

如果存在不能移动的方向,我们也可以列出类似的式子。此外,当 ( x , y ) = ( N − 1 , M − 1 ) (x,y)=(N-1,M-1) (x,y)=(N1,M1) 时,我们有 E ( N − 1 , M − 1 ) = 0 E(N-1,M-1)=0 E(N1,M1)=0 。为了使方程有唯一解,我们令有石头的格子和无法到达终点的格子都有 E ( x , y ) = 0 E(x,y)=0 E(x,y)=0 。把得到的方程联立,就可以求解期望步数了。

代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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<<": "<
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;
}

/*

*/

你可能感兴趣的:(数论,高斯消元)