题面
题意
在n*m的棋盘中有一个机器人在(x,y),每个时刻它会等概率地从不动,向左走,向右走,向下走四种操作中选择一种,但不能走出边界,在边界的机器人的选择数量将变少,问机器人走到最后一行的期望步数是多少。
做法
如果直接进行dp会发现有后效性,因为机器人可以向左也可以向右,但是在行与行之间的转移是没有后效性的,因此可以记dp值表示该点走到最后一行的期望步数,最后一行的dp值为0,然后逐行向上转移,但是因为同一行的转移是有后效性的,所以要用高斯消元来求解,这样就可以得到一个时间复杂度为
O
(
n
∗
m
3
)
O(n*m^3)
O(n∗m3)的算法,但是太慢了。
我们可以观察一下系数矩阵,发现它实际上只有从左上角到右下角一带和最右边的常数列是有数字的,且每行每列均至多只有3个非零系数,因此进行一次高斯消元的时间复杂度并非
O
(
m
3
)
O(m^3)
O(m3),而是
O
(
m
)
O(m)
O(m)的,然后这题就可以做了。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#define db double
#define eps 1e-8
#define N 1010
using namespace std;
int m,n,x,y;
db num[N][N],last[N];
void clear()
{
int i,j;
for(i=1;i<=n;i++)
{
num[i][n+1]=0;
for(j=i-1;j<=i+1;j++)
{
num[i][j]=0;
}
}
}
inline void xy()
{
int i,j,k;
db t;
for(i=1;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
if(fabs(num[j][i])<eps) break;
t=num[j][i]/num[i][i];
for(k=i;k<=n;k++)
{
if(fabs(num[i][k])<eps) break;
num[j][k]-=t*num[i][k];
}
num[j][n+1]-=t*num[i][n+1];
}
}
}
int main()
{
int i,j,k,t;
db tmp;
cin>>m>>n>>x>>y;
if(m==x)
{
puts("0.0000000000");
return 0;
}
for(i=m-1;i>=x;i--)
{
clear();
for(j=1;j<=n;j++)
{
t=4-(j==1)-(j==n);
tmp=1/(db)t;
if(j>1) num[j][j-1]=tmp;
if(j<n) num[j][j+1]=tmp;
num[j][j]=tmp-1;
num[j][n+1]=tmp*last[j]+1;
}
xy();
for(j=n;j>=1;j--)
{
tmp=num[j][n+1];
for(k=j+1;k<=n;k++)
{
if(fabs(num[j][k])<eps) break;
tmp+=num[j][k]*last[k];
}
last[j]=-tmp/num[j][j];
}
}
printf("%.10f",last[y]);
}