Broken robot 题解

发布时间 2023-09-28 23:25:25作者: Idtwtei

题目链接

Rroken robot

分析

\(f[i][j]\) 为从 \(i\)\(j\) 列到最后一行的期望,则

\( f[i][j]= \begin{cases} \frac{1}{3}(f[i][j]+f[i][j+1]+f[i+1][j])+1 &i=1\\ \frac{1}{4}(f[i][j]+f[i][j-1]+f[i,j+1]+f[i+1][j])+1 &1<i<m\\ \frac{1}{3}(f[i][j]+f[i][j-1]+f[i+1][j])+1 &i=m \end{cases} \)

我们发现第 \(i\) 层只与第 \(i+1\) 层有关
可以在每一层中通过高斯消元求解
举个例子
\(m=5\) 时,可以列出如下方程组

\( \begin{bmatrix} &2 &-1 &0 &0 &0 &3f[i+1][1]+1\\ &-1 &3 &-1 &0 &0 &4f[i+1][2]+1\\ &0 &-1 &3 &-1 &0 &4f[i+1][3]+1\\ &0 &0 &-1 &3 &-1 &4f[i+1][4]+1\\ &0 &0 &0 &-1 &2 &3f[i+1][5]+1 \end{bmatrix} \)

发现系数十分有规律,扫上下两边即可求解(具体见代码)
这样一次消元的复杂度为 \(O(M)\)
总的复杂度为 \(O(MN)\)

注意:

高斯-约旦消元的时间复杂度是不正确的
当计算第 \(i\) 行时,第 \(i+1\) 列的所有 \(0\) 都会减一个数
导致计算 \(i+1\) 行时需要将所有行更新一次

代码

#include<bits/stdc++.h>
using namespace std;

#define db double

const int N=1e3+100;

int n,m,x,y;
db f[N],gs[N][N];//f[i]为当前层的第i列

void wk(){
	for(int i=1;i<=m;i++){
		if(i==1||i==m) f[i]+=3.0;
		else f[i]+=4.0;
	}
	for(int i=1;i<=m;i++){//Gauss消元
		db c=gs[i+1][i]/gs[i][i];
		f[i+1]-=f[i]*c;
		gs[i+1][i]-=gs[i][i]*c;
		gs[i+1][i+1]-=gs[i][i+1]*c;
	}
	for(int i=m;i;i--){
		if(i!=m) f[i]+=f[i+1];
		f[i]/=gs[i][i];
	}
}

int main(){
	scanf("%d %d %d %d", &n, &m, &x, &y);
	
	for(int i=1;i<=m;i++) f[i]=0.0;
	
	if(m==1){
		for(int i=n-1;i>=x;i--) f[1]=f[1]+2.0;
		printf("%.4lf", f[1]);
		return 0;
	}
	
	for(int i=n-1;i>=x;i--){//只需计算到x即可
		memset(gs,0,sizeof(gs));
		for(int i=1;i<=m;i++){
			if(i==1||i==m) gs[i][i]=2.0;
			else gs[i][i]=3.0;
			if(i!=1) gs[i][i-1]=-1.0;
			if(i!=m) gs[i][i+1]=-1.0;
		}	
		wk();
	}
	
	printf("%.4lf", f[y]);
	return 0;
}