zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",f[x0][y0]);
      }
      return 0;
    }
  • 相关阅读:
    node03
    node02
    node01
    Vue-router重修02
    Vue-router重修01
    Vue重修02
    VUE重修01
    利用表达式目录树进行实体映射
    C#托管堆和垃圾回收
    C# 异步锁
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6251416.html
Copyright © 2011-2022 走看看