牛客网--蘑菇阵

2023-10-14 12:40
文章标签 牛客 蘑菇

本文主要是介绍牛客网--蘑菇阵,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目描述

现在有两个好友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

分析:

首先想到的办法是,求出从左上角到右下角所有的路径数目,以及不包含一颗蘑菇的路径数目,二者相除,便得到了A碰不到蘑菇走到B的家的概率,这可以用DFS来解决,三下五除二,就一个DFS出来了:

void AwalkDFS(vector<vector<int>> &Map,int i,int j,int PathMushromCount,int &A,int &B){
/*Map:地图i,j:当前位置A:到达右下角没有蘑菇的路径数B:到达右下角有蘑菇的路径数
*/if (i >= 0 && i < Map.size() && j >= 0 && j < Map[0].size()){if (i == Map.size() && j == Map[0].size()){PathMushromCount+Map[i][j]>0 ? ++B : ++A;return;}AwalkDFS(Map, i, j + 1, PathMushromCount + Map[i][j], A, B);//toward the rightAwalkDFS(Map, i+1, j, PathMushromCount + Map[i][j], A, B);//toward the down}
}

完全是瞎做!这样的DFS 是指数时间,因为要穷举所有的可能,没有剪枝,别说答案是否正确,就连时间都不允许!

于是乎,稍加思考,就可以改进为动态规划:

(1) 假设 fij 表示到达 Map[i][j] 的总路径数目,由于每一步只能想右或向下,则它依赖于左边和上边的状态,即到达 fij 的路径数等于到达左边 fi,j1 的路径数加上到达到 fi1,j 的路径数:

fij={fi1,j+fi,j1,i>=1,j>=11,i=0 or j=0(1)

(2) 假设 hij 表示走到 Map[i][j] 没有遇到蘑菇的路径数目,类似于 fij 的分析,它依赖于 Map[i][j] 的状态,及 hi1,j hi,j1 ,状态方程如下

hi,j=0,i=j=0,Map[0][0]=0,()0,Map[i][j]>1,(0)hi1,j+hi,j1,i>=1,j>=1,(,+)h0,j1,i=0,()hi1,0,j=0,()(2)

按照这样的思路,写出代码

double AWalk_dp_1(vector<vector<int>> &Map){int N = Map.size();if (N == 0) return 0;int M = Map[0].size();vector<vector<int>> P(N, vector<int>(M, 0));//fijvector<vector<int>> H = P;  //hij/*initializing*/P[0][0] = 1; H[0][0] = 1;//************1. initialize the first column**************for (int i = 1; i<N; ++i){P[i][0] = 1;H[i][0] = H[i - 1][0];if (Map[i][0]>0) H[i][0] = 0;}//************2. initialize the first row**************for (int j = 1; j<M; ++j){P[0][j] = 1;H[0][j] = H[0][j - 1];if (Map[0][j]>0) H[0][j] = 0;}for (int i = 1; i<N; ++i)for (int j = 1; j<M; ++j){P[i][j] = P[i - 1][j] + P[i][j - 1];if (Map[i][j]>0) H[i][j] = 0;else{H[i][j] = H[i - 1][j] + H[i][j - 1];}}return (double)H[N - 1][M - 1] / P[N - 1][M - 1];
}

然而求解的结果任然是错误的,不过,求解的路径数目是正确的!而且求解路径数目的速度降到了 O(NM) !

进一步分析题目发现,其实发现A走到每一个格子的概率P是不一样的,拿其中一个算例来分析,如图所示:

比如边界上的格子:
(1) 左上角Map[0][0]=0, 到达的概率是1;
(2) 第一行,对于任意P[0][j],它只能从左边一格到达,如果格子仅有一行,A在Map[0][j] 的时候只能走到Map[0][j+1]。
其余边界分析类似…..

对于最右下角(目标)格子,格子Map[N-1][M-2] (下标从0开始)、Map[N-2][M-1] 都只能以1的概率到达此格子。

基于这样的分析,假设 gij 表示 A 到达格子Map[i][j]不碰见蘑菇的概率,同样,除去边界,它可以由 gi1,j gi,j1 表示,所以有以下的状态方程

gij=1,i=j=0,()0Map[i][j]>0,(0)12gi1,j+12gi,j1,i>=1,j>=112g0,j1,i=0,j>=1,N>1,(绿)g0,j1,i=0,j>=1,N=1,(1(i,j))12gi1,0,j=0,i>=1,M>1(绿)gi1,0,j=0,i>=1,M=1,(1(i,j))gi1,j+gi,j1,i=N1,j=M1,N,M>1,()

上面状态方程看似繁杂,但核心思想只有一个:

gijgi1,jgi,j1

只不过不同的位置,这个转换概率不同,所以导致了一系列边界的处理。
最终的代码如下(牛客网测试通过,上图就是所得出的动态规划表数据):

double AWalk_dp(vector<vector<int>> &Map){int N = Map.size();if (N == 0) return 0.0;int M = Map[0].size();if (M == 0) return 0.0;vector<vector<double>> P(N, vector<double>(M, 0));/*initializing*/P[0][0] = 1; //************1. initialize the first column**************for (int i = 1; i<N; ++i){if (M>1) P[i][0] = P[i-1][0]*0.5;else  P[i][0] = P[i - 1][0] * 1.0;if (Map[i][0]>0) P[i][0] = 0.0;}//************2. initialize the first row**************for (int j = 1; j<M; ++j){if (N>1) P[0][j] = P[0][j-1]*0.5;else     P[0][j] = P[0][j - 1] * 1.0;if (Map[0][j]>0) P[0][j] = 0.0;}//************3. calculate no-boundary area ***********for (int i = 1; i<N-1; ++i)for (int j = 1; j<M-1; ++j){if (Map[i][j]>0) P[i][j] = 0.0;else  P[i][j] = 0.5*(P[i - 1][j] + P[i][j - 1]);}//***********4 coping with boundary*********for (int i = 1,j=M-1; j>0 && i < N-1; i++){P[i][j] = 0.5 * P[i][j-1] + P[i - 1][j];if (Map[i][j]>0) P[i][j] = 0.0;}for (int j = 1,i=N-1;i>0 && j < M-1; j++) {P[i][j] = 0.5*P[i - 1][j] + P[i][j-1];if (Map[i][j]>0) P[i][j] = 0.0;}if (N>1 && M>1)P[N - 1][M - 1] =1.0 * P[N-2][M-1] + 1.0*P[N-1][M-2];return  P[N-1][M-1];
}

此题其实是较简单的DP问题,难点在于它是动态规划与概率的结合,只要意识到了到达每个点的概率不同就成功了一半!

最后,总结一点,编程更多需要一种边界思维,好多问题都需要处理好边界,才能使得代码健壮、鲁棒,调试算法程序最快的就是每一步都要有数学思维的参与,这可以很好地解决指针越界等问题!

错误之处,在所难免,不吝赐教!

这篇关于牛客网--蘑菇阵的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/210592

相关文章

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

牛客小白月赛100(A,B,C,D,E,F三元环计数)

比赛链接 官方讲解 这场比较简单,ABC都很签到,D是个不太裸需要预处理的 B F S BFS BFS 搜索,E是调和级数暴力枚举,F是三元环计数。三元环考的比较少,没见过可能会偏难。 A ACM中的A题 思路: 就是枚举每个边变成原来的两倍,然后看看两短边之和是否大于第三边即可。 不能只给最短边乘 2 2 2,比如 1 4 8 这组数据,也不能只给第二短边乘 2 2 2,比

笔试强训,[NOIP2002普及组]过河卒牛客.游游的水果大礼包牛客.买卖股票的最好时机(二)二叉树非递归前序遍历

目录 [NOIP2002普及组]过河卒 牛客.游游的水果大礼包 牛客.买卖股票的最好时机(二) 二叉树非递归前序遍历 [NOIP2002普及组]过河卒 题里面给的提示很有用,那个马的关系,后面就注意,dp需要作为long的类型。 import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息publ

每日OJ_牛客_求和(递归深搜)

目录 牛客_求和(递归深搜) 解析代码 牛客_求和(递归深搜) 求和_好未来笔试题_牛客网 解析代码         递归中每次累加一个新的数,如果累加和大于等于目标,结束递归。此时如果累加和正好等于目标,则打印组合。向上回退搜索其它组合。此题本身就是一个搜索的过程,找到所有的组合。 #include <iostream>#include <cmath>#in

牛客《剑指Offer》 -- 数值的整数次方

题目描述 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。 思路 特别注意负数的情况,出现负数,将其转化为正数然后求倒数。 class Solution {public:double Power(double base, int exponent) {double total = 1;bool flag = false

牛客网《剑指Offer》 二进制中1的个数

题目描述 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 思路 负数用补码,其实就是求一个数据在计算机中是存储是怎么样子的。用位运算,就能很好实现。 class Solution {public:int NumberOf1(int n) {int count = 0;int flag = 1;while (flag != 0) {if ((n & f

牛客网《剑指Offer》 矩形覆盖

题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? class Solution {public:int rectCover(int number) {if(number==0) return 0;if(number==1) return 1;if(number==2) return 2;retu

牛客《剑指Offer》 变态跳台阶

题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 思路 根据 普通的跳台阶可以总结出 f(n) = f(n-1) + f(n-2) +f(n-3) + 。。。。+ f(1) +1 不妨设 f(0) = 1 , 则易得 class Solution {public:int jumpFloorII(int n

牛客《剑指Offer》 跳台阶

题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 思路 递归思想,n阶梯子走法等于n-1 加上n-2的。 class Solution {public:int jumpFloor(int number) {if(number==1) return 1;if(number==2) return 2;return jumpFl