「動態規劃」如何求地下城游戲中,最低初始健康點數是多少?

174. 地下城游戲icon-default.png?t=N7T8https://leetcode.cn/problems/dungeon-game/description/

惡魔們抓住了公主并將她關在了地下城dungeon的右下角。地下城是由m x n個房間組成的二維網格。我們英勇的騎士最初被安置在左上角的房間里,他必須穿過地下城并通過對抗惡魔來拯救公主。騎士的初始健康點數為一個正整數。如果他的健康點數在某一時刻降至0或以下,他會立即死亡。有些房間由惡魔守衛,因此騎士在進入這些房間時會失去健康點數(若房間里的值為負整數,則表示騎士將損失健康點數);其他房間要么是空的(房間里的值為0),要么包含增加騎士健康點數的魔法球(若房間里的值為正整數,則表示騎士將增加健康點數)。為了盡快解救公主,騎士決定每次只向右或向下移動一步。返回確保騎士能夠拯救到公主所需的最低初始健康點數。注意:任何房間都可能對騎士的健康點數造成威脅,也可能增加騎士的健康點數,包括騎士進入的左上角房間以及公主被監禁的右下角房間。

  1. 輸入:dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]],輸出:7,解釋:如果騎士遵循最佳路徑:右 -> 右 -> 下 -> 下 ,則騎士的初始健康點數至少為7。
  2. 輸入:dungeon = [[0]],輸出:1。

提示:m == dungeon.length,n == dungeon[i].length;1 <= m, n <= 200;-1000 <= dungeon[i][j] <= 1000。


我們用動態規劃的思想來解決這個問題。

確定狀態表示:根據經驗和題目要求,我們有2個狀態表示的方案:

  • 用dp[i][j]表示:從起點開始,到達[i, j]位置,所需的最低初始健康點數。
  • 用dp[i][j]表示:從[i, j]位置開始,到達終點,所需的最低初始健康點數。

究竟選擇哪一種狀態表示呢?事實上,哪一種狀態表示能推導出狀態轉移方程,我們就選擇哪一種狀態表示。

推導狀態轉移方程:首先考慮前一種狀態表示。考慮最近的一步,要想到達[i, j]位置,只有2種情況:

  • 先到達[i - 1, j]位置,再向下走一步,到達[i, j]位置。
  • 先到達[i, j - 1]位置,再向右走一步,到達[i, j]位置。

如果能推出狀態轉移方程,那么狀態轉移方程一定形如dp[i][j] = f(dp[i - 1, j], dp[i, j - 1])。然而,[i, j]右下方的位置是有可能影響到dp[i][j]的。比如,如果右下方有一個房間是-1000,那么所需的初始健康點數就是一個很大的值;如果右下方都是正數,那么可能不需要很大的初始健康點數。也就是說,dp[i][j]和右下方的值相關,但是dp[i][j] = f(dp[i - 1, j], dp[i, j - 1])這個方程與右下方的值無關。從而,我們推導不出狀態轉移方程。

所以,我們選擇后一種狀態表示:用dp[i][j]表示:從[i, j]位置開始,到達終點,所需的最低初始健康點數。考慮最近的一步,要想從dp[i][j]位置出發到達終點,只有2種情況:

  • 先向下走一步,到達[i + 1, j]位置,再從[i + 1, j]位置出發到達終點。所以,從[i, j]位置出發到達終點需要的最低初始健康點數dp[i][j],在經歷了[i, j]房間后,健康點數變為dp[i][j] + dungeon[i][j],而dp[i][j] + dungeon[i][j]必須至少是從[i + 1, j]位置出發到達終點所需要的最低初始健康點數dp[i + 1][j],即dp[i][j] + dungeon[i][j] >= dp[i + 1][j],從而dp[i][j] >= dp[i + 1][j] - dungeon[i][j],又由于dp[i][j]表示最低初始健康點數,所以dp[i][j] = dp[i + 1][j] - dungeon[i][j]。
  • 先向右走一步,到達[i, j + 1]位置,再從[i, j + 1]位置出發到達終點。同理可得此時dp[i][j] = dp[i][j + 1] - dungeon[i][j]。

從[i, j]位置出發到達終點所需要的最低初始健康點數,應該是上面2種情況的較小值,即dp[i][j] = min(dp[i + 1][j] - dungeon[i][j], dp[i][j + 1] - dungeon[i][j]) = min(dp[i + 1][j], dp[i][j + 1])?- dungeon[i][j]。

然而這個狀態轉移方程有個很大的漏洞。如果min(dp[i + 1][j], dp[i][j + 1]) <=?dungeon[i][j],那么dp[i][j] =?min(dp[i + 1][j], dp[i][j + 1])?- dungeon[i][j] <= 0。然而血量是不能低于0的,所以我們還需要判斷一下,如果計算出來的dp[i][j] <= 0,那么dp[i][j] = 1。

綜上所述:狀態轉移方程為:dp[i][j] = max(1, min(dp[i + 1][j], dp[i][j + 1])?- dungeon[i][j])

初始化:觀察狀態轉移方程,我們在計算dp表最后一行和最后一列的值時,會越界訪問。所以,我們要對其初始化。這里我們用增加輔助結點的方式來初始化。我們在dp表的最下面和最右邊分別加上一行一列輔助結點。接下來我們考慮,如何初始化輔助結點,才能保證后續的填表是正確的。我們把此時的dp表畫出來:

      ? *? *
? ? ? ? *
* * * * *

先考慮右下角的?位置。這個?位置表示,直接從dungeon的右下角出發,到達右下角,所需要的最低初始健康點數。顯然這個?位置的值只需要保證,在更新完處于dungeon的右下角的健康點數之后,其值依然大于等于1,也就是說,如果dungeon的右下角是正數,那么?位置的值是1;如果dungeon的右下角是負數,那么?位置的值是1減去dungeon的右下角的值(負負得正)。再觀察狀態轉移方程:dp[i][j] = max(1, min(dp[i + 1][j], dp[i][j + 1])?- dungeon[i][j]),我們發現,如果dp[i + 1][j] =?dp[i][j + 1] = 1,那么dp[i][j] = max(1, min(1, 1)?- dungeon[i][j]) =?max(1, 1 - dungeon[i][j]),1代表dungeon的右下角是正數的情況,1 - dungeon[i][j]代表dungeon的右下角是負數的情況,剛好符合預期。所以,對于右下角的?位置,我們要把它的下面和右邊的2個*位置的值初始化為1。

      ? *? *
? ? ? ? 1
* * * 1 *

接著考慮除了右下角的?位置之外,其余的?位置。觀察狀態轉移方程:?dp[i][j] = max(1, min(dp[i + 1][j], dp[i][j + 1])?- dungeon[i][j]),我們發現,dp[i + 1][j]和dp[i][j + 1]會涉及到輔助結點。我們只需要把這些輔助結點初始化為+∞,在計算min(dp[i + 1][j], dp[i][j + 1])時,輔助結點的值就不會影響到結果了。由于并沒有導致溢出風險的運算,我們用INT_MAX代表+∞即可。

綜上所述:我們在dp表的最下面和最右邊分別加上一行一列輔助結點,并且把[m - 1, n]和[m, n - 1]位置的值初始化為1,其余輔助結點初始化為INT_MAX

填表順序:根據狀態轉移方程,dp[i][j]依賴于dp[i + 1][j]和dp[i][j + 1],所以應從下往上,從右往左填表

返回值:應返回dp表左上角的值,即dp[0][0]

細節問題:由于新增了一行一列輔助結點,dp表的規模比dungeon的規模大一行一列,即dp表的規模為(m + 1) x (n + 1)。由于輔助結點是在dp表的右下方,并不影響下標的映射關系,所以dp表的[i, j]位置依然對應dungeon的[i, j]位置。

時間復雜度:O(m x n),空間復雜度:O(m x n)。

class Solution {
public:int calculateMinimumHP(vector<vector<int>>& dungeon) {int m = dungeon.size(), n = dungeon[0].size();// 創建dp表vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX));// 初始化dp[m - 1][n] = dp[m][n - 1] = 1;// 填表for (int i = m - 1; i >= 0; i--) {for (int j = n - 1; j >= 0; j--) {dp[i][j] =max(1, min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]);}}// 返回結果return dp[0][0];}
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/24004.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/24004.shtml
英文地址,請注明出處:http://en.pswp.cn/web/24004.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Text2SQL 論文】C3:使用 ChatGPT 實現 zero-shot Text2SQL

論文&#xff1a;C3: Zero-shot Text-to-SQL with ChatGPT ???? arXiv:2307.07306&#xff0c;浙大 Code&#xff1a;C3SQL | GitHub 一、論文速讀 使用 ChatGPT 來解決 Text2SQL 任務時&#xff0c;few-shots ICL 的 setting 需要輸入大量的 tokens&#xff0c;這有點昂貴…

基于GLM生成SQL,基于MOSS生成SQL,其中什么是GLM 什么是MOSS

GLM 和 MOSS 是兩種不同的模型或系統&#xff0c;通常用在自然語言處理 (NLP) 和生成任務中&#xff0c;如生成 SQL 查詢。讓我們逐個解釋它們的含義和用途&#xff1a; GLM (Generalized Language Model) GLM 是一種通用語言模型&#xff0c;設計用于處理和生成自然語言。以…

MacOS M系列芯片一鍵配置多個不同版本的JDK

第一步&#xff1a;下載JDK。 官網下載地址&#xff1a;Java Archive | Oracle 選擇自己想要下載的版本&#xff0c;一般來說下載一個jdk8和一個jdk11就夠用了。 M系列芯片選擇這兩個&#xff0c;第一個是壓縮包&#xff0c;第二個是dmg可以安裝的。 第二步&#xff1a;編輯…

eclipse插件開發(二)RCP第三方庫的引入方式

RCP第三方庫的引入 最近在RCP開發過程中遇到JSON串與對象互轉的問題&#xff0c;如何像spring開發模式一樣引入第三方庫呢&#xff1f;eclipse插件開發中用到p2庫&#xff0c;但也支持maven庫的引入。關鍵在于.target這個關鍵文件。 .target 文件用于定義一個目標平臺&#x…

民主測評要做些什么?

民主測評&#xff0c;作為一種重要的民主管理工具&#xff0c;旨在通過廣泛征求群眾意見&#xff0c;對特定對象或事項進行客觀、公正的評價。它不僅是推動民主參與、民主監督的重要手段&#xff0c;也是提升治理效能、促進社會和諧的有效途徑。以下將詳細介紹民主測評的主要過…

常見的布局方法及優缺點

頁面布局常用的方法有浮動、定位、flex、grid網格布局、柵格系統布局 浮動&#xff1a; 優點&#xff1a;兼容性好。 缺點&#xff1a;浮動會脫離標準文檔流&#xff0c;因此要清除浮動。我們解決好這個問題即可。 絕對定位 優點&#xff1a;快捷。 缺點&#xff1a;導致子…

如何以非交互方式將參數傳遞給交互式腳本

文章目錄 問題回答1. 使用 Here Document2. 使用 echo 管道傳遞3. 使用文件描述符4. 使用 expect 工具 參考 問題 我有一個 Bash 腳本&#xff0c;它使用 read 命令以交互方式讀取命令參數&#xff0c;例如 yes/no 選項。是否有一種方法可以在非交互式腳本中調用這個腳本&…

vue用vite配置代理解決跨域問題(target、rewrite和changeOrigin的使用場景)

Vite的target、rewrite和changeOrigin的使用場景 1. target 使用場景&#xff1a;target 屬性在 Vite 的 vite.config.ts 或 vite.config.js 文件的 server.proxy 配置中指定&#xff0c;用于設置代理服務器應該將請求轉發到的目標地址。這通常是一個后端服務的API接口地址。…

Chrome 源碼閱讀:跟蹤一個鼠標事件的流程

我們通過在關鍵節點打斷點的方式&#xff0c;去分析一個鼠標事件的流程。 我們知道chromium是多進程模型&#xff0c;那么&#xff0c;我們可以推測&#xff1a;一個鼠標消息先從主進程產生&#xff0c;再通過跨進程通信發送給渲染進程&#xff0c;渲染進程再發送給WebFrame&a…

【FAS】《CN103106397B》

原文 CN103106397B-基于亮瞳效應的人臉活體檢測方法-授權-2013.01.19 華南理工大學 方法 / 點評 核心方法用的是傳統的形態學和模板匹配&#xff0c;亮點是雙紅外發射器做差分 差分&#xff1a;所述FPGA芯片控制兩組紅外光源&#xff08;一近一遠&#xff09;交替亮滅&…

[力扣題解] 700. 二叉搜索樹中的搜索

題目&#xff1a;700. 二叉搜索樹中的搜索 思路 觀察法 二叉搜索樹的搜索操作&#xff0c;比較根節點的數值&#xff0c; 如果等于&#xff1a;找到了&#xff1b;大于根節點&#xff1a;在右子樹&#xff0c;往右走&#xff1b;小于根節點&#xff1a;在左子樹&#xff0c;…

【Java基礎】線程方法

start()&#xff1a;啟動線程&#xff0c;使線程進入就緒狀態。 run()&#xff1a;線程執行的代碼邏輯&#xff0c;需要重寫該方法。 停止線程 void interrupt() 中斷線程&#xff0c;讓它重新去爭搶cpu 如果目標線程長時間等待&#xff0c;則應該使用interrupt方法來中斷等待…

RDMA (2)

iWARP(RDMA)怎么工作的 招式1:bypass內核 非iWARP時,當應用向網絡適配器發出讀或者寫命令時,命令穿過用戶空間以及內核空間,因此需要在用戶空間和內核空間間進行切換。 iWARP使用RDMA,讓應用直接將命令送達到網絡適配器。這規避了對內核的調用,減少了開銷和延遲。 招式2…

【Kubernetes】三證集齊 Kubernetes實現資源超賣(附鏡像包)

目錄 插敘前言一、思考和原理二、實現步驟0. 資料包1. TLS證書簽發2. 使用 certmanager 生成簽發證書3. 獲取secret的內容 并替換CA_BUNDLE4.部署svc deploy 三、測試驗證1. 觀察pod情況2. 給node 打上不需要超售的標簽【可以讓master節點資源不超賣】3. 資源實現超賣4. 刪除還…

[補題記錄]Leetcode 209.長度最小的子數組

傳送門&#xff1a;長度最小的子數組 Problem/題意 給定一個整數數組和一個整數 target&#xff0c;要求算出數組中最小長度的連續子數組&#xff0c;數組元素的和大于等于 target。 Thought/思路 題目要求維護最小的長度&#xff0c;因此我們希望&#xff1a;當條件不滿足…

IP域名關系的研究與系統設計(學習某知名測繪系統)

IP域名關系庫管理包括域名庫檢索和whois庫檢索&#xff0c;詳情如下。 域名庫檢索支持以下5項功能&#xff1a; 1.通過過濾器檢索 篩選條件包含IP地址、口令、工具名稱、可利用的漏洞編號、創建時間&#xff1b; 2.通過關鍵字檢索 在查詢框中輸入域名庫名稱的部分關鍵詞&a…

計算機組成結構—IO系統概述

目錄 一、I/O 系統的發展 1. 早期階段 2. 接口模塊和 DMA 階段 3. 通道結構階段 4. 處理機階段 二、I/O 系統的組成 1. I/O 軟件 2. I/O 硬件 三、I/O 設備 1. I/O 設備分類 2. I/O 設備的組成 在計算機中&#xff0c;除 CPU 和主存兩大模塊之外&#xff0c;第三個重…

Apple開發者應用商店(AppStore)描述文件及ADHOC描述文件生成

創建AD HOC描述文件 1.選中Profiles,然后點擊加號創建 2.創建已注冊設備可安裝描述文件 3.選擇要注冊的id 4.選擇證書 5.選擇設備 6.輸入文件名,點擊生成 7.生成成功,點擊下載

TCP為什么握手是三次,而揮手是四次

TCP&#xff08;傳輸控制協議&#xff09;使用三次握手&#xff08;3WHS&#xff09;來建立一個可靠的連接&#xff0c;并使用四次揮手&#xff08;4WHS&#xff09;來終止連接。以下是每個步驟的詳細解釋&#xff1a; 三次握手&#xff08;3WHS&#xff09;建立連接&#xff…

solidity的modifier修飾符

solidity的modifier修飾符 什么是modifier修飾符 修飾器&#xff08;modifier&#xff09;是solidity特有的語法&#xff0c;類似于面向對象編程中的decorator&#xff0c;聲明函數擁有的特性&#xff0c;并減少代碼冗余。 Solidity 中關鍵字 modifier 用于聲明一個函數修改…