CodeForces 24D Broken Robot

题意:n*m的棋盘,一个机器人在(i,j)处,每次等概率地停在原地,向左移动一格,向右移动一格,向下移动一格(不能移出棋盘).求走到最后一行所需期望步数.n<=1000,m<=1000

一个看起来可以用来DP的顺序是永远只能从上面走到下面,但同一行之间的转移会出现环.如果n和m的范围稍微小一点,我们可以像SDOI走迷宫一题跑一个分层的高斯消元,但这个题的范围比较大,会超时,但这道题的背景暗示我们列出来的方程组会比较规则,我们不妨先把方程列出来看看有什么特点.

设F[i][j]为从第i行第j列走到最后一行的期望步数

则有

F[i][1]=1+F[i][1]/3+F[i][2]/3+F[i+1]/3

F[i][j]=1+F[i][j]/4+F[i][j-1]/4+F[i][j+1]/4+F[i+1][j]/4,2<=j<=m-1

F[i][m]=1+F[i][m-1]/3+F[i+1][m]/3+F[i][m]/3

不妨仍考虑从第i+1行推到第i行.那么我们在求解F[i][1…m]的时候应当已经知道F[i+1][1…m],边界显然是F[n][j]=0

对于一行,有两个方程只有两个未知数,m-2个方程有三个未知数

如果两个方程里面是同两个未知数,我们可以直接解出这两个未知数,但现在F[i][1]和F[i][m]的未知数并不一定是同两个,我们可以考虑对这些式子进行变形.

F[i][1]的方程和F[i][1],F[i][2]有关,F[i][2]的方程和F[i][1],F[i][2],F[i][3]有关,那么我们可以用这两个方程消掉F[i][1],得到关于F[i][2]和F[i][3]的二元一次方程,接下来再和F[i][3]的方程相消,可以得到关于F[i][3]和F[i][4]的方程….最终我们就可以解出最右端的F[i][m],此后顺着推回来,复杂度O(n).

实现的时候我把每个二元一次方程表示成F[i][j]=a[j]*F[i][j+1]+b[j]的形式,比较容易理解.

转移的式子可能需要简单推一下,见代码.

注意m=1的情况要特判.

#include<cstdio>
double f[1005][1005];
double a[1005],b[1005];
int main(){
  int n,m,x0,y0;scanf("%d%d%d%d",&n,&m,&x0,&y0);
  if(m==1){
    a[n]=0;
    for(int i=n-1;i>=x0;--i){
      a[i]=a[i+1]+2;
    }
    printf("%.4f\n",a[x0]);
  }else{    
    for(int i=1;i<=m;++i)f[n][i]=0;
    for(int i=n-1;i>=x0;--i){
      a[1]=0.5;b[1]=f[i+1][1]/2+1.5;
      for(int j=2;j<m;++j){
    b[j]=b[j-1]/4.0+f[i+1][j]/4.0+1.0;
    a[j]=0.25;
    a[j]/=(0.75-a[j-1]/4.0);b[j]/=(0.75-a[j-1]/4.0);
      }
      f[i][m]=(b[m-1]+f[i+1][m]+3.0)/(2-a[m-1]);
      for(int j=m-1;j>=1;--j)f[i][j]=b[j]+a[j]*f[i][j+1];
    }
    printf("%.4f\n",f[x0][y0]);
  }
  return 0;
}

 

转载于:https://www.cnblogs.com/liu-runda/p/6251416.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值