题目如下:
给定一个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)步数的期望(向右)。

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

被折叠的 条评论
为什么被折叠?



