【每日算法Day 65】你能顺利救出地下城里的公主吗?

简介: LeetCode 174. 地下城游戏[1]

题目链接


LeetCode 174. 地下城游戏[1]

题目描述


一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。

为了尽快到达公主,骑士决定每次只向右或向下移动一步。

编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。

例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。

-2(K) -3 -3
-5 -10 1
10 30 -5(P)

提示:

  • 骑士的健康点数没有上限。
  • 任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

题解


错误解法


首先我们肯定想到的是从左上到右下动态规划,那么对于  这个格子来说,它有两个选择,可以从  或者  过来。

我们令  表示从左上角走到  这个格子所需要的最小生命值,那么我们选择  ,也就是两个来向中较小的那个走过来。但是考虑了当前格子的数值之后,路线上所需生命的最小值是可能增大的,而这时候可能选择两个来向中较大的那个反而更好(因为那个来向数值之和比较大),所以这里就产生了矛盾,无法求解。

举个简单的例子:

1(K) -3 3
0 -2 0
-3 -3 -3(P)

这个例子中如果只看走到格子  的结果的话,肯定是 下 -> 右 -> 右 最好,因为这样初始生命只需要 2 就够了。而另一条路 右 -> 右 -> 下 则需要初始生命 3 。

但是如果继续走到格子  ,那么最优方向一定是从  过来(另一个方向负数太多)。但是到  的最优路线保存的是 下 -> 右 -> 右 这一条,走到终点总和是 -4 ,初始所需最小生命增大为 5 。而另一条原本不怎么好的路线 右 -> 右 -> 下 总和是 -2 ,初始所需最小生命 3 ,所以仍然保持不变。

这样看来原本不好的路线在最后的结果里是可能会变好的,所以不好保存下来直接递推。

正确解法


既然从左上到右下没法动态规划,我们不妨从右下到左上动态规划看看。

我们令  表示从  这个格子走到右下角所需要的最小生命值,同样我们选择两个去向中的较小值  。然后考虑了格子  之后,  就更新为:

为什么这里选择两个去向中所需初始生命较小的那个就没问题了呢?

严格证明


image.png

考虑上图这种情况,这里我把  抽象为了  ,右边一格抽象为了  ,右下角抽象为了  。然后  走下面这条路所需初始生命值最小,路径上格子记为  ,另一条路径上格子记为  。

因为走路径  所需的初始生命值更小,所以我们有:

等价于:

这时候我们在两边  里面同时加上  ,大小关系是不会变的。

而错误解法中,考虑下图这种情况:image.png

同样我们可以得到:

到这里为止和上面正确解法是一模一样的。但是,加上  之后,和上面正解的区别就是,正解求和里每一项都加了,所以大小关系不变,但是错解只有一项加了(就是所有值全加起来),大小关系无法确定

代码


c++

classSolution {
public:  
intcalculateMinimumHP(vector<vector<int>>&dungeon) {  
intn=dungeon.size(), m=dungeon[0].size();  
vector<vector<int>>dp(n+1, vector<int>(m+1, INT_MAX));  
dp[n][m-1] =dp[n-1][m] =1;   
for (inti=n-1; i>=0; --i) {  
for (intj=m-1; j>=0; --j) { 
intminn=min(dp[i+1][j], dp[i][j+1]); 
dp[i][j] =max(1, minn-dungeon[i][j]);  
            }     
        }     
returndp[0][0];  
    }
};

参考资料

[1]

LeetCode 174. 地下城游戏: https://leetcode-cn.com/problems/dungeon-game/

image.png

作者简介:godweiyang知乎同名华东师范大学计算机系硕士在读,方向自然语言处理与深度学习喜欢与人分享技术与知识,期待与你的进一步交流~


相关文章
|
前端开发 开发工具 git
mac前端开发环境
mac前端开发环境
376 0
|
Web App开发 存储 安全
SameSiteCookie疑难杂症
mPaas下SameSiteCookie疑难问题处理
1166 0
SameSiteCookie疑难杂症
|
10月前
|
机器学习/深度学习 人工智能 监控
为什么选择工作流引擎?三大主流引擎优缺点剖析
工作流引擎是一种用于自动化、管理和监控业务流程的软件系统,通过预定义规则和流程模型协调任务流转。其核心功能包括流程建模、任务分配、状态跟踪和异常处理,能提升企业流程效率30%-50%,减少80%以上的人为错误。典型应用场景涵盖审批、生产、服务和决策类流程。主流引擎如Activiti、Flowable和Camunda各有特色,Camunda因高性能和完整工具链成为复杂项目的首选。未来趋势包括低代码集成、AI优化及云原生架构。
为什么选择工作流引擎?三大主流引擎优缺点剖析
|
10月前
|
数据采集 资源调度 JavaScript
极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图
本文介绍了使用 Vue Flow 绘制流程图的方法与技巧。Vue Flow 是一个灵活强大的工具,适合自定义复杂的流程图。文章从环境要求(Node.js v20+ 和 Vue 3.3+)、基础入门案例、自定义功能(节点与连线的定制、事件处理)到实际案例全面解析其用法。重点强调了 Vue Flow 的高度灵活性,虽然预定义内容较少,但提供了丰富的 API 支持深度定制。同时,文中还分享了关于句柄(handles)的使用方法,以及如何解决官网复杂案例无法运行的问题。最后通过对比 mermaid,总结 Vue Flow 更适合需要高度自定义和复杂需求的场景,并附带多个相关技术博客链接供进一步学习。
|
监控 前端开发 JavaScript
前端稳定性工具-Sentry
【11月更文挑战第9天】Sentry 是一个开源的错误和性能监控平台,支持多种编程语言和框架。它能够捕获前端应用中的各种错误和性能问题,提供详细的错误信息和用户行为关联,帮助开发团队快速定位和解决问题,优化应用性能。但需注意隐私保护、数据准确性和成本控制。
1889 3
|
前端开发 Java API
Swagger接口文档 —— 手把手教学,全方位超详细小白能看懂,百分百能用Java版
本文提供了一份详细的Swagger接口文档生成工具的使用教程,包括了导入依赖、配置类设置、资源映射、拦截器配置、Swagger注解使用、生成接口文档、在线调试页面访问以及如何设置全局参数(如token),旨在帮助Java开发者快速上手Swagger。
9590 0
Swagger接口文档 —— 手把手教学,全方位超详细小白能看懂,百分百能用Java版
|
传感器 编解码 监控
线程池有哪些拒绝策略?
本文介绍了Java线程池的四种拒绝策略:AbortPolicy(默认策略,抛出异常)、CallerRunsPolicy(调用者运行任务)、DiscardPolicy(丢弃任务,不抛异常)和DiscardOldestPolicy(丢弃最旧任务,尝试提交当前任务)。每种策略都有其适用的业务场景,并通过代码示例进行了说明。选择合适的策略取决于具体的应用需求和对任务处理的优先级。
1052 0
|
JavaScript
JS将阿拉伯数字翻译成中文的大写数字、JS将数字转换为大写金额(整理)
JS将阿拉伯数字翻译成中文的大写数字、JS将数字转换为大写金额(整理)
|
移动开发 前端开发 JavaScript
css模块化的方案
css模块化的方案
309 0
|
JavaScript Java 关系型数据库
Mac不会用?玩转brew,部署web开发环境【jdk、git、msyql、maven、node】全家桶,前后端覆盖
Mac不会用?玩转brew,部署web开发环境【jdk、git、msyql、maven、node】全家桶,前后端覆盖
1555 0
Mac不会用?玩转brew,部署web开发环境【jdk、git、msyql、maven、node】全家桶,前后端覆盖

热门文章

最新文章