【CLYZ集训】千与千寻【期望DP】【高斯消元】

119 篇文章 0 订阅
11 篇文章 0 订阅

题目大意

给你一个n*m的平面,从(0,0)随机往上或右走。走到上边界时可以穿到最下边,到右边界时可以穿到最左边。问走到(x,y)的期望步数。

思路:

将左边界和下边界上的格子设为元,然后所有的格子的期望值都可以用这些元来表示。然后高斯消元解除元的值,回带到x,y就可以了

c o d e code code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>

using namespace std;

const int MAXN = 510;

int n, m, S, T, tot;
double f[MAXN][MAXN][MAXN], g[MAXN][MAXN];
int b[MAXN][MAXN];

void add(int x, int y, int x1, int y1) {
	for(int i = 0; i <= n + m - 2; i ++)
		f[x][y][i] += (f[x][y1][i] + f[x1][y][i]) / 2.0;
}

void Gauss() {
	int l;
	double tmp;
	for(int i = 0; i < tot; i ++) {
		l = i;
		for(int j = i + 1; j < tot; j ++) 
			if(fabs(g[l][i]) < fabs(g[j][i])) l = j;
		if(i != l) for(int j = i; j <= tot; j ++) swap(g[l][j], g[i][j]);
		tmp = g[i][i];
		for(int j = i; j <= tot; j ++) g[i][j] /= tmp;
		for(int j = 0; j < tot; j ++)
			if(i != j) {
				tmp = g[j][i];
				for(int k = i; k <= tot; k ++) g[j][k] -= g[i][k] * tmp;
			}
	}
}

int main() {
//	freopen("walk.in", "r", stdin);
//	freopen("walk.out", "w", stdout);
	scanf("%d%d%d%d", &n, &m, &S, &T);
	for(int i = 1; i < n; i ++) f[i][0][tot] = 1.0, b[i][0] = tot, tot ++;
	for(int i = 1; i < m; i ++) f[0][i][tot] = 1.0, b[0][i] = tot, tot ++;
	for(int i = 1; i < n; i ++) {
		for(int j = 1; j < m; j ++) {
			int x = i - 1, y = j - 1;
			add(i, j, x, y);
			f[i][j][n + m - 2] += 1.0;
		}
	}
	for(int i = 1; i < n; i ++) {
		int x = b[i][0];
		for(int k = 0; k <= n + m - 3; k ++) f[i][0][k] -= f[i][m - 1][k] / 2.0;
		if(i != 1) f[i][0][b[i - 1][0]] -= 0.5;
		f[i][0][n + m - 2] = f[i][0][n + m - 2] + f[i][m - 1][n + m - 2] / 2.0 + 1.0;
		for(int k = 0; k <= n + m - 2; k ++) g[x][k] = f[i][0][k];
	}
	for(int j = 1; j < m; j ++) {
		int x = b[0][j];
		for(int k = 0; k <= n + m - 3; k ++) f[0][j][k] -= f[n - 1][j][k] / 2.0;
		if(j != 1) f[0][j][b[0][j - 1]] -= 0.5;
		f[0][j][n + m - 2] = f[0][j][n + m - 2] + f[n - 1][j][n + m - 2] / 2.0 + 1.0;
		for(int k = 0; k <= n + m - 2; k ++) g[x][k] = f[0][j][k];
	}
	Gauss();
	if(S == 0 || T == 0) {
		printf("%.18lf", g[b[S][T]][n + m - 2]);
		return 0;
	}
	double ans = 0;
	for(int i = 0; i <= n + m - 3; i ++)
		ans += f[S][T][i] * g[i][n + m - 2];
	printf("%.18lf", ans + f[S][T][n + m - 2]);
	return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值