题目描述:
题目链接: 蘑菇阵_牛客题霸_牛客网 (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];
}
}