【编程题 动态规划】BD4 蘑菇阵(详细注释 易懂)

题目描述:

   题目链接: 蘑菇阵_牛客题霸_牛客网 (nowcoder.com)

 现在有两个好友A和B,住在一片长有蘑菇的由n*m个方格组成的草地,A在(1,1),B在(n,m)。现在A想要拜访B,由于她只想去B的家,所以每次她只会走(i,j+1)或(i+1,j)这样的路线,在草地上有k个蘑菇种在格子里(多个蘑菇可能在同一方格),问:A如果每一步随机选择的话(若她在边界上,则只有一种选择),那么她不碰到蘑菇走到B的家的概率是多少?

输入描述:

第一行N,M,K(1 ≤ N,M ≤ 20, k ≤ 100),N,M为草地大小,接下来K行,每行两个整数x,y,代表(x,y)处有一个蘑菇。

输出描述:

输出一行,代表所求概率(保留到2位小数)

示例1

输入:
2 2 1
2 1

输出:

0.50

题目解读:

    有一个N行M列的方格,好友A总是从坐标(1,1)处出发,好友B总是在坐标(N,M)处,好友A每次只能向右或者向下走一步,但是在到达的这千山万水路程中,还有蘑菇,这个蘑菇有毒,好友A不能经过有蘑菇的小方格, 但由于好友A是凭感觉走路,所以到达一个方格后,再去下一个方格都具有不确定性。 请问好友A在这样的不确定性的前进中 ,不踩到蘑菇中毒死去,而到达好友B家的概率是多少?  

    好了,题目解读完了, 现在说一下这个题中两处比较狗的地方,一个是 很容易忽略他说的随机问题,我们就直接想到 先计算 没有蘑菇到达终点有多少条路,再计算 没有蘑菇到达终点有多少条路,然后两者相除得到结果 ;如果是这么想的,那就错了,因为每个方格处都是不确定性的,所以概率会被累计,这和直接相除得到的概率差别是非常大的。  然后第二个比较狗的地方,就是它全程不提 多组输入,但其实测试用例中给的都是多组输入,如果你没有多组输入的语句,导致只能给出一组数据的计算结果,那你就会看到 通过率 0%  。

解题思想: 动态规划

     上面说了要避坑的地方,那就来说如何解题。 首先题目说给一个N行M列的方格,那我们能想到的就是用一个二维数组来接收,但由于题目中给的坐标是从1开始的,为了方便,我们就设一个N+1行,M+1列的二维数组(暂且称为 草地数组),  然后它后面是给出蘑菇的坐标,那我们就把蘑菇的坐标位置设置为1(二维数组初始化后,所有值默认为0)。

    然后我们为了求得最终的概率,就要记录它每一步的概率,所以我们也设置一个二维数组,为了便于记录,也设为 N+1行,M+1列的二维数组(暂且称为 概率数组)。 然后A家所在的位置 (1,1)概率设为1,但除了(1,1)以外的所有方格,它的概率需要从左边方格和上边方格来求(因为题目说了 A只能往下或者往右走)。

   以下图中 坐标(2,2)--也就是A的右下方为例,它的概率就是 坐标(2,1)的概率 + 坐标(1,2)的概率。 那坐标(1,2)的概率如何求呢,同理 它等于 1(这是A的概率)* 0.5(从A可以往下或往右,所以到达(1,2)的概率是0.5) +0(这是坐标(0,2)的概率,初始化后默认为0 )*0(从坐标(0,2)到(1,2)概率是1,因为只能往下走) ,去掉注释简写下 到达坐标(1,2)的概率 = 1*0.5+0*0 = 0.5  ,同理也就可以求得其他方格的概率。  那蘑菇怎么处理呢,通过上面的概率求得当前方格的概率之后,我们从草地数组中检查当前方格的值是否为1 ,如果是1 表示是蘑菇,那就把这个方格的概率再赋值为0就好了。

  (大家看完这个题,可以看看我的 最长公共子序列那个题,这两个题的解法非常相似)

   通过上面的思路说明,我们知道 如果要 求每个方格的概率,就要求它上面和左边方格的概率之和, 但是在最后一行或者最后一列,概率就是1,因为A只能往下走或者往右走。 所以我们得到下面的状态方程:

  P(i,j)= P( i-1, j ) * ( j==m ?1:0.5 ) + P( i,j-1 ) *( i==n ? 1 :0.5)

j == m, 表示 如果是往下走,但在最后一列,概率就是1 ,i == n 表示 如果是往右走,但在最后一行,概率就是1 。

 好了,现在根据状态方程,写代码:

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        while(scan.hasNextInt()){
            int n = scan.nextInt();
           int m = scan.nextInt();
            int k = scan.nextInt();
        
        int[][] grassLand = new int[n+1][m+1];
        for(int i=0;i<k;i++){
            int x = scan.nextInt();
            int y = scan.nextInt();
            grassLand[x][y]=1;
        }
        double num = getProbability(grassLand,n,m);
        System.out.printf("%.2f\n",num);
        }
    }
    public static double getProbability(int[][] grassLand,int n,int m){
        double[][] probability = new double[n+1][m+1];
        probability[1][1]=1.0;
        for(int i=1;i<= n;i++){
            for(int j=1;j<=m;j++){
       // 如果不是坐标(1,1)就用状态方程计算概率
                if(!(i==1 && j==1)){
                    probability[i][j]= probability[i-1][j]*(j==m?1:0.5)+
                        probability[i][j-1]*(i==n? 1:0.5);
                }
           // 概率计算完之后,从草地数组中检查,它的值是否为1 ,如果是1 表示是蘑菇,
              // 那就给它赋值为 0 
                if(grassLand[i][j]==1){
                    probability[i][j]=0.0;
                }
            }
        }
        return probability[n][m];
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值