题目描述
给定5个参数,N,M,row,col,k表示在N * M
的区域上,醉汉Bob初始在(row,col)位置
Bob一共要迈出k步,且每步都会等概率向上下左右四个方向走一个单位
任何时候Bob只要离开N* M的区域,就直接死亡
返回k步之后,Bob还在N * M的区域的概率
题目解析
思路
class Solution {
std::vector<int> dx {-1, 1, 0, 0};
std::vector<int> dy {0, 0, -1, 1};
// 递归含义: 站在当前位置上,还剩下rest步要走,走完之后能够活下来的概率
// 先判断自己在不在棋盘上,如果在,那么看看还能不能走
double process(int x, int y, int rest, int N, int M){
// 如果已经跳出棋盘了,那么Bob一定死掉了,此时活着的概率是0.0
if(x < 0 || y < 0 || x >= N || y >= M){
return 0.0;
}
// 如果还在棋盘上
if(rest == 0){ // 但是已经没有步数了,此时活下来的概率是1.0
return 1.0;
}
// 如果还在棋盘上,而且有步数可以走。
// 那么活下来的概率 = (向上走还活着的概率 + 向下走还活着的概率 + 向左走还活着的概率 + 向右走还活着的概率) / 4
double save = 0;
for (int i = 0; i < 4; ++i) {
save += process(x + dx[i], y + dy[i], rest - 1, N, M);
}
return save / 4;
}
public:
double Bob(int row, int col, int k, int N, int M){
return process(row, col, k, N, M);
}
};
思路
暴力递归
一共要走k步,每次有四种方向可以选择,那么全部走完之后一共
4
K
4^K
4K种可能性,所有的选择可以过程如下四叉树
- 如果走完K步还活着,那么就生存点数+1
- 如果走完k步发现死了,那么生存点数不变
- 如果没有走完k步就死了,生存点数也不变,提前返回
最终使用 ( 生存点数 ) / 4 K (生存点数) / 4^K (生存点数)/4K,就是最终的概率
class Solution {
std::vector<int> dx {-1, 1, 0, 0};
std::vector<int> dy {0, 0, -1, 1};
int process(int x, int y, int rest, int N, int M){
if(x < 0 || y < 0 || x >= N || y >= M){
return 0;
}
if(rest == 0){
return 1;
}
int save = 0;
for (int i = 0; i < 4; ++i) {
save += process(x + dx[i], y + dy[i], rest - 1, N, M);
}
return save;
}
public:
double Bob(int row, int col, int k, int N, int M){
assert(N >= 0 && M >= 0 && k >= 0);
int save = process(row, col, k, N, M);
int all = std::pow(4, k);
return (double ) save / (double ) all;
}
};
暴力递归改动态规划
(1)准备一个数组。我们先观察递归函数的可变参数和变化范围
int process(int x, int y, int rest, int N, int M)
- x:0~N
- y:0~M
- rest:0~K
因此要准备一个三维数组:
int dp[N + 1][M + 1][K + 1];
(2)返回值。看主函数是如何调用递归的
int save = process(row, col, k, N, M);
因此,需要一个dp[row][col][k]
(3)填表
(3.1) base case初始化表
if(rest == 0){
return 1;
}
所以应该初始化第一层: d p [ . . . ] [ . . . ] [ 0 ] = 1 dp[...][...][0] = 1 dp[...][...][0]=1
(3.2)考虑一般情况
for (int i = 0; i < 4; ++i) {
save += process(x + dx[i], y + dy[i], rest - 1, N, M);
}
由于rest - 1
可以看出上一层依赖下一层的四个方向的值,所以应该第一层是rest,然后再看xy;而xy随便怎么填都可以(反正第一层已经填好了)。因此:
for(int rest = 1; rest <= K; rest++){
}
(5)综上
class Solution {
std::vector<int> dx {-1, 1, 0, 0};
std::vector<int> dy {0, 0, -1, 1};
int process(int x, int y, int rest, int N, int M){
if(x < 0 || y < 0 || x >= N || y >= M){
return 0;
}
if(rest == 0){
return 1;
}
int save = 0;
for (int i = 0; i < 4; ++i) {
save += process(x + dx[i], y + dy[i], rest - 1, N, M);
}
return save;
}
static long pick( std::vector<std::vector<std::vector<int>>> &dp ,int x, int y, int rest, int N, int M){
if(x < 0 || y < 0 || x >= N || y >= M){
return 0;
}
return dp[x][y][rest];
}
public:
double Bob(int row, int col, int K, int N, int M){
std::vector<std::vector<std::vector<int>>> dp(N + 1,
std::vector<std::vector<int>>(M + 1,
std::vector<int>(K + 1, 0)));
// 初始化第一层
for (int i = 0; i <= N; ++i) {
for (int j = 0; j <= M; ++j) {
dp[i][j][0] = 1;
}
}
for (int rest = 1; rest <= K; ++rest) {
for (int x = 0; x <= N; ++x) {
for (int y = 0; y <= M; ++y) {
for (int i = 0; i < 4; ++i) {
dp[x][y][rest] += pick(dp, x + dx[i], y + dy[i], rest - 1, N, M); //凡是+都要小心溢出
}
}
}
}
int save = dp[row][col][K];
int all = std::pow(4, K);
return (double ) save / (double ) all;
}
};
测试
Bob(2, 1, 6, 4, 4); //0.384277