原文转载自「刘悦的技术博客」https://v3u.cn/a_id_168

现在很多互联网企业学聪明了,知道应聘者有目的性的刷Leetcode原题,用来应付算法题面试,所以开始对这些题进行“魔改”,比如北京某电商平台的这道题:

有一个正方形的岛,使用二维方形矩阵表示,岛上有一个醉汉,每一步可以往上下左右四个方向之一移动一格,如果超出矩阵范围他就死了,假设每一步的方向都是随机的(因为他是醉的),请计算n步以后他还活着的概率。

例如:输入矩阵大小2*2,起点(0,0),随机走出一步 n = 1  

输出0.5  也就是有一半的几率还活着  

例如:输入矩阵大小3*3,起点(1,1),随机走出一步 n = 1  

输出1  也就是百分之百还活着

乍一看有点懵,但是提取关键字:二维矩阵、上下左右四个方向、矩阵范围、n步,有没有感到很熟悉?刷过Leetcode的同学一定已经联想到了Leetcode原题第576题:出界的路径数,难度等级为中等。

给定一个 m × n 的网格和一个球。球的起始坐标为(i,j),你可以将球移到相邻的单元格内,或者往上、下、左、右四个方向上移动使球穿过网格边界。但是,你最多可以移动N次。找出可以将球移出边界的路径数量。答案可能非常大,返回 结果 mod 109+ 7 的值。

和魔改版的题联系起来,所谓醉汉“死了”,其实就是移出边界,而每走一步都会有四种可能,所以所谓的“存活率”也就是当我们算出移出边界的路径数量之后,再除以方向的基数4,就可以算出“存活率”,相反也可以推算“死亡率”,归根结底,魔改版题的题眼还是算出移出边界的路径数,并不是最后问的“存活率”问题,这题只是用了一个并不是很讲究的障眼法,很有可能是该电商平台老板让手下的某个研发出道算法题招人用,而该研发已经被需求搞的晕头转向,无奈之下随便从leetcode复制了一道出来,随便改了改。

至于解法,下意识想到并且非常好理解的解法就是利用BFS(Breadth First Search 广度优先),因为醉汉最多只能移动N次,我们只要bfs依次遍历如果发现出界,就代表死亡,进行累加1,当bfs的深度大于N的时候break结束。理论上是没有任何问题。

import collections
def how_likely_alive(m,n,N,i,j):
mod = 10**9 + 7
Q = collections.deque([(i,j,0)])
res = 0
while Q:
x,y,step = Q.popleft()
if step > N: break
if 0<=x<m and 0<=y<n:
Q.append((x+1,y,step+1))
Q.append((x-1,y,step+1))
Q.append((x,y+1,step+1))
Q.append((x,y-1,step+1))
else:
res += 1
num = res % mod
if num == 0:
return 1
else:
return num / 4 print(how_likely_alive(2,2,1,0,0))

一般情况下,如果该岗位的技术要求并不高,使用bfs基本就算过关了,但是如果面试官想来一次压力面试(所谓压力面试就是想探探你的底),看看你的极限在哪里,就会要求你用效率更高的算法来解题。(这里需要简单分辨一下压力面试还是故意刁难,压力面试如果不会的话,礼貌询问就能拿到答案,而如果连面试官都不知道面试的答案,那肯定就是故意刁难了,也就没有面下去的必要了)。

我们再回到题目中想一想,魔改版题目并没有定义醉后随机走的步数N的范围,假设N的取值范围达到了50,我们对任意一个坐标点bfs有四个方向进行遍历,同时考虑往回走的可能性,那么复杂度达到了N的四倍,这个效率显然不会令人满意,所以当N相对小的情况下,比如只走1步,bfs是最优解,而范围过大就需要考虑dp了。

dp(Dynamic Programming)算法即是业界大名鼎鼎的动态规划算法了,其核心思路是把一个复杂的大问题拆成若干个子问题,通过解决子问题来逐步解决大问题,是不是和分治法有点像?关于分治算法可以参考这篇文章:当我们谈论算法我们在谈论什么:由疫情核酸检测想到的分治算法(Divide-and-Conquer),但是和分治法有区别的地方是,使用动态规划思想有个前提:当且仅当每个子问题都是离散的(即每个子问题都不依赖于其他子问题时),才能使用动态规划。

再次回到题目,假设这个醉汉在第 N 步到达 (mi, nj) 位置有 dp[N][mi][nj] 种路径,可以假设一下当前状态如何从上一步移动中得来。其实就是上下左右四个方向移动过来的,而移动步数则是 N-1。

def how_likely_alive(m, n, N, i, j):  

    tmp=[[[0 for i in range(n)] for j in range(m)] for k in range(N+1)]
for k in range(1,N+1):
for p in range(m):
for q in range(n):
if 0==p:
up=1
else:
up=tmp[k-1][p-1][q]
if m-1==p:
down=1
else:
down=tmp[k-1][p+1][q]
if 0==q:
left=1
else:
left=tmp[k-1][p][q-1]
if n-1==q:
right=1
else:
right=tmp[k-1][p][q+1]
tmp[k][p][q]=(up+down+left+right)%1000000007 num = tmp[N][i][j]
if num == 0:
return 1
else:
return num / 4
return num print(how_likely_alive(2,2,1,0,0))

结语:Leetcode算法题浩如烟海,想要每一道题都了如指掌,个人感觉难度不小,但是从这道二维矩阵中的醉汉来看,企业就算想要“魔改”,也是万变不离其宗,多多少少都有迹可循,所以我们在刷题的过程中,应该本着宁缺毋滥的原则,真实的掌握算法核心思想,才能够做到举一反三、百战不殆。

原文转载自「刘悦的技术博客」 https://v3u.cn/a_id_168

Python算法之动态规划(Dynamic Programming)解析:二维矩阵中的醉汉(魔改版leetcode出界的路径数)的更多相关文章

  1. [算法总结] 动态规划 (Dynamic Programming)

    本文组织结构如下: 前言 最长公共子序列(LCS) 最长不下降子序列(LIS) 最大连续子序列之和 最长回文子串 数塔问题 背包问题(Knapsack-Problem) 矩阵链相乘 总结 前言 在学过 ...

  2. 01二维矩阵中最大全为1的正方形maxSquare——经典DP问题(二维)

    在一个二维01矩阵中找到全为1的最大正方形 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 以矩阵中每一个点作为正方形右下角点来处理,而以该点为右下角点的最大边长最多比 ...

  3. java代码生成二维码以及解析二维码

    package com.test; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedI ...

  4. Leetcode之二分法专题-240. 搜索二维矩阵 II(Search a 2D Matrix II)

    Leetcode之二分法专题-240. 搜索二维矩阵 II(Search a 2D Matrix II) 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target.该矩阵 ...

  5. [Leetcode] search a 2d matrix 搜索二维矩阵

    Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the follo ...

  6. 动态规划(Dynamic Programming)算法与LC实例的理解

    动态规划(Dynamic Programming)算法与LC实例的理解 希望通过写下来自己学习历程的方式帮助自己加深对知识的理解,也帮助其他人更好地学习,少走弯路.也欢迎大家来给我的Github的Le ...

  7. 算法导论学习-Dynamic Programming

    转载自:http://blog.csdn.net/speedme/article/details/24231197 1. 什么是动态规划 ------------------------------- ...

  8. 动态规划Dynamic Programming

    动态规划Dynamic Programming code教你做人:DP其实不算是一种算法,而是一种思想/思路,分阶段决策的思路 理解动态规划: 递归与动态规划的联系与区别 -> 记忆化搜索 -& ...

  9. 6专题总结-动态规划dynamic programming

    专题6--动态规划 1.动态规划基础知识 什么情况下可能是动态规划?满足下面三个条件之一:1. Maximum/Minimum -- 最大最小,最长,最短:写程序一般有max/min.2. Yes/N ...

随机推荐

  1. nodejs + koa + typescript 集成和自动重启

    版本说明 Node.js: 16.13.1 全局安装 TypeScript yarn global add typescript 创建项目 创建如下目录结构 project ├── src │ └── ...

  2. 2.0 vue循环和方法调用

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. 原理:C++为什么一般把模板实现放入头文件

    写在前面 本文通过实例分析与讲解,解释了为什么C++一般将模板实现放在头文件中.这主要与C/C++的编译机制以及C++模板的实现原理相关,详情见正文.同时,本文给出了不将模板实现放在头文件中的解决方案 ...

  4. 《Unix 网络编程》14:高级 I/O 函数

    高级 I/O 函数 ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ...

  5. 【clickhouse专栏】单机版的安装与验证

    <clickhouse专栏>第三节内容,先安装一个单机版的clickhouse,是后续学习多副本或者分布式集群安装的基础内容.但基本的clickhouse是不依赖于zookeeper的,只 ...

  6. 1.数据结构《Pytorch神经网络高效入门教程》Deeplizard

    当移动一个数组或向量时,我们需要一个索引:二维数组/矩阵需要两个索引, 比如说标量是零维张量,数组/向量/矢量是一维张量,矩阵是是二维张量,n维数组是n维张量. 如果我们被告知,  假设有一个张量t, ...

  7. 聊聊消息中间件(1),AMQP那些事儿

    开篇 说到消息队列,相信大家并不陌生.大家在日常的工作中其实都有用过.相信大部分的研发在使用消息队列的过程中也仅仅是停留在用上面,里面的知识点掌握得并不是很系统,有部分强大的功能可能由于本身公司的业务 ...

  8. Django——表单

    一.前言 看了下教程,以为表单很简单呢,结果搞了一两个钟才弄懂,哈哈哈,所以说不要小瞧每一件事. 先说明下HTTP请求: HTTP协议以"请求-回复"的方式工作.客户发送请求时,可 ...

  9. awk运用三维数组进行插值获得任意经纬度处的水层沉积层地壳厚度

    awk三维数组与插值 目的:给定经纬度,获得该点地下的冰层水层沉积层和地壳的厚度 实现:awk一行命令 下载Crust1.0模型 该数据集的详细介绍见官网. 解压后有几个文件:crust1.vp,cr ...

  10. JS:object

    object:对象 1.对象是拥有属性和方法的数据,也是一个变量,但值有多个,以key-value的形式. 2.对象有继承属性: 每当创建一个对象,对象里面就会有一个原型对象prototype,可以从 ...