一道笔试题(华为2021校园招聘)

本文详细解析了一个关于在带有概率的n*m表格中,从左上角到右下角路径步数期望的算法问题。通过实例说明如何计算各格子到相邻格子的期望步数,并采用动态规划思想优化计算过程。

题目如下:
给定一个n*m的表格,其中每个格子有三个数 pD pR pS 分别代表向下 向右 停在原地的概率,满足pD + pR + pS = 1,求从表格左上角到右下角的步数的期望。
输入示例:
2 2
1.0 0.0 0.0 0.5 0.5 0.0
0.0 0.5 0.5 0.5 0.5 0.0
这个意思是 第一行分别是n和m的值
第二行前三个分别是格子坐标为(0,0)的 pD pR pS ,后三个分别是格子坐标为(0,1)的pD pR pS;
第三行类似;
输出:
3
输出为步数的期望。

首先解释下这个示例:(考试的时候这个示例都没想明白)
从(0,0)只能到(1,0)所以期望为1;
从(1,0)到(1,1)的期望设为E;
下表表示往右一个格子走的步数的概率:
在这里插入图片描述
在这里插入图片描述
这个公式的推导我会在后面说(面试时碰到这个无穷级数求和 简直崩了 题目确实难)所以这个示例的答案为1+2 = 3
为了一般化,现在假设一个格子的概率 pD = a pR = b pS = c,现在得出其往下一个格子的期望 (记为Ed)和 往右一个格子的期望(记为Er)。
往下一个格子走的步数的概率如下:
在这里插入图片描述
在这里插入图片描述
同理
在这里插入图片描述
以上内容属于高等数学无穷级数求和部分 不清楚的同学可以去翻翻书。
下面开始讲讲做这个题的思路,如果直接从期望的公式入手,从左上角到右下角的步数 以及每一种的步数的期望 这样会比较难,比如假设步数为n+m-2,这种简单情况 就是不停留,(其实就是不同的路径 把每个格子到下一个格子概率相乘),这种情况还好点 ,如果停留了,那么就复杂了 ,暂不分析了。
给出另外一种思路 其实就是示例中的思路 ,首先计算出当前格子到右边格子的期望 和 到下边格子的期望 (套公式),然后计算每条路径的期望,最后把每条路径的期望相加。
代码如下:

#include <iostream>
#include <vector>

void ComputeE(std::vector<std::vector<std::vector<float>>>& p, std::vector<std::vector<std::vector<float>>> &e,int n,int m)
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			float tmp = 1 - p[i][j][2];
			float product = tmp * tmp;
			e[i][j][0] = p[i][j][0] / product;
			e[i][j][1] = p[i][j][1] / product;

			//std::cout << e[i][j][0] << " " << e[i][j][1] << std::endl;
		}
	}
}

//返回 到达格子(i,j)步数的期望(不计算在(i,j)兜圈子的步数)
//建立状态转移方程
//建立dp数组 dp[i][j]表示从左上角到达坐标(i,j)的步数的期望
float recur(std::vector<std::vector<std::vector<float>>> &e,std::vector<std::vector<float>> &memo, int i, int j)
{
	if (i == 0 && j == 0)
	{
		return 0;
	}
	if (memo[i][j] != -1)
	{
		return memo[i][j];
	}
	if (i > 0 && e[i - 1][j][0] > 0 && j > 0 && e[i][j - 1][1] > 0)
	{
		memo[i][j] = recur(e, memo, i - 1, j) + e[i - 1][j][0] + recur(e, memo, i, j - 1) + e[i][j - 1][1];
	}
	else if (i > 0 && e[i - 1][j][0] > 0)
	{
		memo[i][j] = recur(e, memo, i - 1, j) + e[i - 1][j][0];
	}
	else if (j>0 && e[i][j - 1][1] > 0)
	{
		memo[i][j] = recur(e, memo, i, j - 1) + e[i][j - 1][1];
	}
	return memo[i][j];
}
int main()
{
	int n, m;
	std::cin >> n >> m;
	//建立一个n*m*3 的矩阵存储 概率数据
	std::vector<std::vector<std::vector<float>>> p(n, std::vector<std::vector<float>>(m, std::vector<float>(3, 0)));
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			for (int k = 0; k < 3; k++)
			{
				std::cin >> p[i][j][k];
			}
		}
	}

	//建立一个n*m*2的矩阵 其中e[i][j][0]存储向下一个格子步数的期望 e[i][j][1]存储向右一个格子步数的期望 
	std::vector<std::vector<std::vector<float>>> e(n, std::vector<std::vector<float>>(m, std::vector<float>(2, 0)));
	ComputeE(p, e, n, m);

	//存储到达每一个格子的步数的期望
	//建立备忘录memo降低复杂度 也可以用dp做
	std::vector<std::vector<float>> memo(n, std::vector<float>(m, -1));

	float len = recur(e, memo,n - 1, m - 1);


	std::cout << len << std::endl;

	//for (int i = 0; i < n; i++)
	//{
	//	for (int j = 0; j < m; j++)
	//	{
	//		std::cout << memo[i][j] << " ";
	//	}
	//	std::cout << std::endl;
	//}
	return 0;
}

对其中dp过程具体阐述一下:
dp数组定义为:
dp[i][j]定义为从左上角到达坐标(i,j)的步数的期望(不计算在(i,j)兜圈子的步数),下面的公式中E[i-1][j][0] 表示从坐标(i-1,j)到坐标(i,j)步数的期望(向下) ,E[i][j-1][1]表示从(i,j-1)到(i,j)步数的期望(向右)。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值