CODEFORCE 24D : Broken robot(高斯消元 + 期望DP)

题目大意:有一个 n ∗ m n * m nm的网格图,有一个机器人在 ( x , y ) (x,y) (x,y)位置,它可以等概率的选择向右,向左,向下或原地不动,如果它在第一列,那它不会有向左这个选择,如果它在最后一列,那么它不会有向右这个选择,问机器人走到最后一行的期望步数。

题解:期望DP,以每一层为一个阶段,对于第 i i i层, d p [ i ] [ j ] dp[i][j] dp[i][j] 表示第 i 行,第 j 列的期望步数。
j = 1 j = 1 j=1 d p [ i ] [ j ] = 1 3 ( d p [ i ] [ j ] + d p [ i ] [ j + 1 ] + d p [ i + 1 ] [ j ] ) + 1 dp[i][j] = \frac{1}{3}(dp[i][j] + dp[i][j + 1] + dp[i + 1][j]) + 1 dp[i][j]=31(dp[i][j]+dp[i][j+1]+dp[i+1][j])+1
1 &lt; j &lt; m 1 &lt; j &lt; m 1<j<m d p [ i ] [ j ] = 1 4 ( d p [ i ] [ j ] + d p [ i ] [ j − 1 ] + d p [ i ] [ j + 1 ] + d p [ i + 1 ] [ j ] ) + 1 dp[i][j] = \frac{1}{4}(dp[i][j] + dp[i][j - 1] + dp[i][j + 1] + dp[i + 1][j]) + 1 dp[i][j]=41(dp[i][j]+dp[i][j1]+dp[i][j+1]+dp[i+1][j])+1
j = m j = m j=m d p [ i ] [ j ] = 1 3 ( d p [ i ] [ j ] + d p [ i ] [ j − 1 ] + d p [ i + 1 ] [ j ] ) + 1 dp[i][j] = \frac{1}{3}(dp[i][j] + dp[i][j - 1] + dp[i + 1][j]) + 1 dp[i][j]=31(dp[i][j]+dp[i][j1]+dp[i+1][j])+1
对于 层数 而言,转移方程没有后效性,状态不会成环,但对于列数而言,某一行的状态成环。
对于状态成环的DP,将要求的DP设为变量,转移式子就变成了一个多元一次方程。

以行为阶段,一行m列则有m个方程,求出这一行所有列的答案,依次递推求上去,每一行内部使用高斯消元, d p [ i + 1 ] [ j ] dp[i + 1][j] dp[i+1][j] 是已求过的行,视为常量。高斯消元本是 ( m 3 ) (m ^ 3) (m3)的复杂度,但这里因为转移只设计到3个变量,整个矩阵除主对角线,主对角线左右相邻的位置外其它位置均为0,可以在O(m)时间内完成消元求解,整体复杂度为O(n * m)。

注意边界问题,当 m = 1时,机器人只能呆在原地或向下走,构造矩阵的方式要改变。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<math.h>
using namespace std;
const double eps = 1e-11;
const int maxn = 1e3 + 10;
int n,m,x,y;
double dp[maxn][maxn];
double a[maxn][maxn],b[maxn];
int main() {
	scanf("%d%d",&n,&m);
	memset(dp,0,sizeof dp);
	scanf("%d%d",&x,&y);
	for(int i = n - 1; i >= 1; i--) {
		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;
			b[j] = -1 - dp[i + 1][j] / 4;
		}
		if(m >= 2) {
			a[m][m] = a[1][1] = -2.0 / 3,a[1][2] = a[m][m - 1] = 1.0 / 3;
			b[1] = -1 - dp[i + 1][1] / 3;
			b[m] = -1 - dp[i + 1][m] / 3;
		}
		else {
			a[m][m] = a[1][1] = -1.0 / 2;
			b[1] = -1 - dp[i + 1][1] / 2;
			b[m] = -1 - dp[i + 1][m] / 2;
		}
		for(int j = 1; j < m; j++) {
			if(fabs(a[j + 1][j]) < eps) continue;
			double rate = a[j + 1][j] / a[j][j];
			for(int k = 0; k < 2; k++)
				a[j + 1][j + k] -= a[j][j + k] * rate;
			b[j + 1] -= b[j] * rate;
		}
		for(int j = m; j > 1; j--) {
			if(fabs(a[j - 1][j]) < eps) continue;
			double rate = a[j - 1][j] / a[j][j];
			a[j - 1][j] -= a[j][j] * rate;
			b[j - 1] -= b[j] * rate;
		}		
		for(int j = 1; j <= m; j++)
			dp[i][j] = b[j] / a[j][j];
	}
	printf("%.10lf\n",dp[x][y]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值