CF24D Broken robot(DP 的后效性处理模板题)

题意

给定一个 \(n\)\(m\) 列的棋盘,给出起点 \((x,y)\),要走到棋盘的最后一行 ,每次等概率向左、右、下走或原地不动,不能走到棋盘外面 ,求走到最后一步的期望步数。

思路

\(f[i][j]\) 表示从 \((i,j)\) 走到最后一行的期望步数,那么最终的答案就是 \(f[x][y]\) ,由于无法走出棋盘,状态转移方程需要分三种情况讨论:

\(j=1\) 时,\(f[i][j]=\dfrac{f[i][j]+f[i+1][j]+f[i][j+1]}{3}+1\)

\(1<j<m\) 时,\(f[i][j]=\dfrac{f[i][j]+f[i+1][j]+f[i][j+1]+f[i][j-1]}{4}+1\)

\(j=m\) 时,\(f[i][j]=\dfrac{f[i][j]+f[i+1][j]+f[i][j-1]}{3}+1\)

同一行中的 \(f[i][j]\) 具有后效性,无法通过递推的方式直接求出。

通过观察可以发现,一行可以列出 \(m\) 个方程。如果先求出下一行的期望步数,那么 \(f[i+1][j]\) 就是一个常量,于是就只有 \(m\) 个未知数,也就是一个 \(m\) 元一次方程组,可以用高斯消元求解

首先对方程进行常数项分离,可以得到增广矩阵:

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

便于理解,这里给出的是当 \(m=5\) 时的特殊情况。

一般的高斯消元时间复杂度为 \(O(n^3)\)
但是可以发现,这里的一次项系数第一行和最后一行有两个系数不为零,第二行到倒数第二行每一行都有三个系数不为零。那么在高斯消元的过程中就省去了寻找第 \(i\) 列最大系数的过程。

同时每次向下消元只需要消除下面一行即可,同时每行只有消除三个数,所以最终本题高斯消元的时间复杂度就只有 \(O(m)\)。总的时间复杂度就为 \(O(nm)\),可以通过本题。

同时需要注意,当 \(m=1\) 时,只能向下走或原地不动,于是 \(f[i][j]=\dfrac{f[i][j]+f[i+1][j]}{2}+1\)

整理可以得到,\(f[i][j]=f[i+1][j]+2\)。于是最终的答案就是 \(2*(n-x)\) ,特判输出即可。

还有一些高斯消元的细节见代码。

code:

#include<iostream>
using namespace std;
const int M=1010;
int n,m,x,y;
double a[M][M],f[M][M];
void YZXAKIOI()
{
	for(int i=1;i<=m;i++)//这里是m,因为是将每一行做一遍高斯消元 
	{
		double r=a[i][i];//不需要寻找最大系数了 
		a[i][i]/=r,a[i][i+1]/=r;
		if(i<m) a[i][m+1]/=r;//如果不特判,最后一行最后一列就会被除两次 
		int d[3]={i,i+1,m+1};//下一行的本列,下一行本列的右边,和最右边的常数项需要相减 
		double t=a[i+1][i];
		for(int j=0;j<3;j++)
		    a[i+1][d[j]]-=a[i][d[j]]*t;
	}
	for(int i=m;i>=1;i--)
	{
		a[i-1][m+1]-=a[i-1][i]*a[i][m+1];
		a[i-1][i]-=a[i][i]*a[i-1][i];
	}
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&x,&y);
	if(m==1) printf("%.4lf\n",2.0*(n-x));	
	else
	{
		for(int i=n-1;i>=1;i--)//注意倒序,因为第n行的期望步数一定为0,故直接从倒数第二行开始求解
		{
			a[1][1]=2.0/3,a[1][2]=-1.0/3,a[1][m+1]=1+f[i+1][1]/3;
			a[m][m]=2.0/3,a[m][m-1]=-1.0/3,a[m][m+1]=1+f[i+1][m]/3;
			for(int j=2;j<m;j++) a[j][j]=3.0/4,a[j][j-1]=a[j][j+1]=-1.0/4,a[j][m+1]=f[i+1][j]/4+1;
			YZXAKIOI();
			for(int j=1;j<=m;j++) f[i][j]=a[j][m+1];
		}
		printf("%.4lf\n",f[x][y]);
	}
	return 0;
}
posted @ 2021-06-22 11:20  曙诚  阅读(55)  评论(0)    收藏  举报