算法:Bob生还的概率

本文探讨了一个有趣的问题:在给定的N*M网格中,醉汉Bob从(row,col)出发,随机行走k步,如何计算他在不越界的情况下生存概率。通过递归、暴力搜索和动态规划的方法,提供了详细的求解步骤和优化算法。测试示例显示Bob在(2,1)位置行走6步,4x4区域内的生存概率为0.384277。
摘要由CSDN通过智能技术生成

题目描述

给定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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值