動態規劃--Day03--打家劫舍--198. 打家劫舍,213. 打家劫舍 II,2320. 統計放置房子的方式數

動態規劃–Day03–打家劫舍–198. 打家劫舍,213. 打家劫舍 II,2320. 統計放置房子的方式數

今天要訓練的題目類型是:【打家劫舍】,題單來自@靈艾山茶府。

掌握動態規劃(DP)是沒有捷徑的,咱們唯一能做的,就是投入時間猛猛刷題。

動態規劃要至少刷100道才算入門!

記憶化搜索是新手村神器。方便理解,寫完之后可以轉譯成遞推。

但是有些題目只能寫遞推,才能優化時間復雜度。熟練之后直接寫遞推也可以。

198. 打家劫舍

方法:記憶化搜索(遞歸)

思路:

和爬樓梯幾乎是同一個模板的。爬樓梯是memo[i] = res1 + res2;,而打家劫舍是memo[i] = Math.max(res1, res2);

后來忽然發現了一個細微的差別,爬樓梯,緩存一般是開n+1位,而打家劫舍緩存一般開n位就夠了。

  1. 使用memo記憶,緩存已經探索過的節點
  2. 從數組最后一個值開始往前探索
    • 遞歸結束判斷:如果索引超出范圍,則返回
    • 如果memo有值,用緩存值
    • 對于當前探索的i這個節點,能不能偷,有兩個情況:
      • 情況一:偷i-1家,那么i家就不能偷(遞歸進去探索i-1節點)
      • 情況二:偷i-2家,那么i家也能偷(遞歸進去探索i-2節點)
      • 返回兩種情況的較大值,順便保存到記憶
class Solution {public int rob(int[] nums) {int n = nums.length;// 記憶(緩存),默認未使用標志為-1int[] memo = new int[n];Arrays.fill(memo, -1);// 從索引n-1開始搜索(也就是數組的最后一個值)return dfs(n - 1, nums, memo);}private int dfs(int i, int[] nums, int[] memo) {// 索引超出范圍,返回if (i < 0) {return 0;}// 如果已經判斷過這種情況了,直接用記憶(緩存)if (memo[i] != -1) {return memo[i];} else {// 情況一:偷i-1家,那么i家就不能偷// 情況二:偷i-2家,那么i家也能偷int res1 = dfs(i - 1, nums, memo);int res2 = dfs(i - 2, nums, memo) + nums[i];// 返回兩種情況的較大值,順便保存到記憶memo[i] = Math.max(res1, res2);}return memo[i];}
}

從dfs函數來看,一進去先判斷索引是否超出范圍,超出了則返回。其實是浪費多了一次資源(因為調用函數要壓棧,出棧等,時間空間都變慢了)

可以改成這樣:先判斷索引,再考慮調用函數:

private int dfs(int i, int[] nums, int[] memo) {if (memo[i] != -1) {return memo[i];} else {// 先判斷索引合法,再調用函數int res1 = 0;if (i - 1 >= 0) {res1 = dfs(i - 1, nums, memo);}// res2要賦值為nums[i]int res2 = nums[i];if (i - 2 >= 0) {res2 = dfs(i - 2, nums, memo) + nums[i];}memo[i] = Math.max(res1, res2);}return memo[i];
}

注意,這里res2要默認賦值為nums[i]。因為這是一個決策,是決定偷i-2,那么i家先偷了,再去i-2,然后發現i-2沒有人住(超出索引范圍),那么i家我也偷到了。

如果寫成這樣是錯的:

private int dfs(int i, int[] nums, int[] memo) {if (memo[i] != -1) {return memo[i];} else {int res1 = 0;int res2 = 0;if (i - 1 >= 0) {res1 = dfs(i - 1, nums, memo);}if (i - 2 >= 0) {res2 = dfs(i - 2, nums, memo) + nums[i];}memo[i] = Math.max(res1, res2);}return memo[i];
}

方法:動態規劃(遞推)

思路:

遞推要初始化,因為f[i]需要依賴f[i-1]和f[i-2],所以要初始化前兩個數,還要特判n==1的情況。

class Solution {public int rob(int[] nums) {int n = nums.length;// 如果只有一家,直接偷,返回。if (n == 1) {return nums[0];}int[] f = new int[n];// 遞推要初始化,因為f[i]需要依賴f[i-1]和f[i-2],所以要初始化前兩個數f[0] = nums[0];f[1] = Math.max(nums[0], nums[1]);// 從第三個數開始遍歷for (int i = 2; i < n; i++) {// 情況一:偷i-1家,那么i家就不能偷// 情況二:偷i-2家,那么i家也能偷int res1 = f[i - 1];int res2 = f[i - 2] + nums[i];// 偷較大值f[i] = Math.max(res1, res2);}// 最后的結果,在最后一家判斷完之后,包含了所有的情況。// 所以答案在最后一個索引的位置。return f[n - 1];}
}

思路:

把f數組索引直接+2。上一篇代碼的f[0]的值,放到這一題代碼的f[2]的位置。

不管f[0]和f[1],直接不用他們。

class Solution {public int rob(int[] nums) {int n = nums.length;int[] f = new int[n + 2];for (int i = 0; i < n; i++) {f[i + 2] = Math.max(f[i + 1], f[i] + nums[i]);}return f[n + 1];}
}

思路:

空間優化的寫法。

class Solution {public int rob(int[] nums) {int f0 = 0;int f1 = 0;for (int x : nums) {int newF = Math.max(f1, f0 + x);f0 = f1;f1 = newF;}return f1;}
}

這些都是寫完之后的優化版本,如果第一次寫,不要想復雜的,先按照自己內心第一思路,把題目寫完先,再想優化。

213. 打家劫舍 II

方法:動態規劃(遞推)

思路:

  • 特判索引0位置,把剩余位置變為非循環數組進行操作。
  • 索引0位置只有兩種選擇,偷或者不偷。
    • 偷索引0,那么索引1和索引n-1不能偷。問題變為從索引2到n-2的非循環數組。
    • 不偷索引0。問題變為從索引1到n-1的非循環數組。
  • 取二者較大值
class Solution {public int rob(int[] nums) {int n = nums.length;// 偷索引0,那么索引1和索引n-1不能偷。問題變為從索引2到n-2的非循環數組。int res1 = nums[0] + rob1(nums, 2, n - 2);// 不偷索引0。問題變為從索引1到n-1的非循環數組。int res2 = rob1(nums, 1, n - 1);// 取二者較大值return Math.max(res1, res2);}// 上一題的空間優化版本private int rob1(int[] nums, int start, int end) {int f0 = 0;int f1 = 0;// [start,end] 左閉右閉for (int i = start; i <= end; i++) {int newF = Math.max(f1, f0 + nums[i]);f0 = f1;f1 = newF;}return f1;}
}

2320. 統計放置房子的方式數

方法:動態規劃(遞推)

思路:

  • 兩邊互相獨立,求一邊,最后f[n] * f[n]即可。
  • 初始化f[0]和f[1]。如果沒有空地,只有不放這1種選擇;如果只有一塊空地,有放和不放2種選擇。
  • 對于每個f[i]:
    • 情況一:如果不放f[i]的話,f[i-1]可放可不放
    • 情況二:如果放f[i]的話, f[i-2]可放可不放
    • 兩種情況加起來,就是f[i]可放可不放
  • 最后返回結果時:
    • 注意,兩個MOD相加,不會溢出int。但是兩個MOD相乘,會溢出int
    • 所以要先轉為long,相乘,取模,后再轉為int
class Solution {public int countHousePlacements(int n) {// 兩邊互相獨立,求一邊,最后f[n] * f[n]即可final int MOD = 1000000007;int[] f = new int[n + 2];// 初始化。如果沒有空地,只有不放這1種選擇;如果只有一塊空地,有放和不放2種選擇。f[0] = 1;f[1] = 2;for (int i = 2; i <= n; i++) {// 情況一:如果不放f[i],f[i-1]可放可不放// 情況二:如果放f[i], f[i-2]可放可不放// 兩種情況加起來,就是f[i]可放可不放f[i] = (f[i - 1] + f[i - 2]) % MOD;}// 注意,兩個MOD相加,不會溢出int。但是兩個MOD相乘,會溢出int// 所以要先轉為long,相乘,取模,后再轉為intreturn (int) ((long) f[n] * f[n] % MOD);}
}

總感覺這道題跟《打家劫舍》沒什么關系,反而像是《爬樓梯》。每個位置都可爬可不爬,然后結果加起來。f[i] = (f[i - 1] + f[i - 2])。初步是這么想,多做題,回頭再看看。

關于取模運算,可以看@靈艾山茶府的這篇文章:模運算的世界:當加減乘除遇上取模。

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

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

相關文章

Nuxt.js@4 中管理 HTML <head> 標簽

可以在 nuxt.config.ts 中配置全局的 HTML 標簽&#xff0c;也可以在指定 index.vue 頁面中配置指定的 HTML 標簽。 在 nuxt.config.ts 中配置 HTML 標簽 export default defineNuxtConfig({compatibilityDate: 2025-07-15,devtools: { enabled: true },app: {head: {charse…

UCIE Specification詳解(十)

文章目錄4.5.3.7 PHYRETRAIN&#xff08;物理層重訓練&#xff09;4.5.3.7.1 Adapter initiated PHY retrain4.5.3.7.2 PHY initiated PHY retrain4.5.3.7.3 Remote Die requested PHY retrain4.5.3.8 TRAIN ERROR4.5.3.9 L1/L24.6 Runtime Recalibration4.7 Multi-module Link…

電商數據的獲取方式:API、爬蟲、第三方服務及更多

在競爭激烈的電商領域&#xff0c;數據是驅動業務增長的關鍵。準確、及時地獲取電商數據&#xff0c;并進行深入分析&#xff0c;能夠幫助企業洞察市場趨勢、優化運營策略、提升用戶體驗。本文將全面介紹電商數據的獲取方式&#xff0c;涵蓋API接口、網絡爬蟲技術、第三方數據服…

《WINDOWS 環境下32位匯編語言程序設計》第8章 通用對話框

Windows操作系統為一些常用功能提供了一些通用對話框&#xff08;Common Dialog Box&#xff09;&#xff0c;比如&#xff0c;在不同的應用程序中進行打開文件、選擇字體、選擇顏色等操作時&#xff0c;不同程序顯示的對話框的模樣都是一樣的。另外&#xff0c;把同樣的應用程…

SOME/IP-SD協議中組播IP地址和端口號應從何處獲取、由誰設置?

<摘要> AUTOSAR SOME/IP-SD協議中組播通信參數的核心配置規則明確規定了在服務端傳輸&#xff08;Server-Transmits&#xff09;和客戶端傳輸&#xff08;Client-Transmits&#xff09;兩種模式下&#xff0c;組播IP地址和端口號應從何處獲取、由誰設置&#xff0c;從而確…

DAY49打卡

追到第45天內容浙大疏錦行

十四、測試 (Testing)

Rust內置了強大的測試框架,使得編寫和運行測試變得非常簡單。Rust的測試系統主要包括單元測試、集成測試和文檔測試。 1. 單元測試 單元測試通常放在與被測試代碼相同的文件中,使用#[cfg(test)]模塊和#[test]屬性標記。 1.1 基本測試結構 // 在src/lib.rs或任何模塊中pub…

LeetCode 刷題【56. 合并區間】

56. 合并區間 自己做 解&#xff1a;排序合并 class Solution { public:static bool compare(const vector<int> &p1, const vector<int> &p2){ //按第一個數排序return p1[0] < p2[0]; }vector<vector<int>> merge(ve…

DistributedLock 實現.Net分布式鎖

在分布式系統中&#xff0c;經常會遇到多個實例同時訪問同一份資源的情況&#xff0c;例如&#xff1a; ? 多個服務節點同時寫入數據庫同一行數據? 定時任務在多個節點上同時運行&#xff0c;導致重復執行? 多實例寫緩存時出現數據覆蓋問題 為了解決 并發沖突 和 數據一致…

Flutter:ios打包ipa,證書申請,Xcode打包,完整流程

步驟1 - 5 為 申請ios的簽名文件&#xff0c;App ID&#xff0c;證書&#xff0c;描述文件&#xff0c;并添加測試打包設備。 步驟1&#xff1a;生成證書簽名文件&#xff08;打開鑰匙串訪問>證書助理>從證書頒發機構請求證書&#xff09; 存儲后得到了一個簽名文件&…

Shell 秘典(卷二)——號令延展秘術 與 流程掌控心法?if 天機判語篇精解

文章目錄前言一、命令擴展詳解1.1 邏輯運算符1.1.1 邏輯與運算符&#xff08;&&&#xff09;1.1.2 邏輯或運算符&#xff08;||&#xff09;1.1.3 組合使用注意事項1.2 echo 命令1.2.1 基本用法1.2.2 輸出到標準錯誤&#xff08;stderr&#xff09;1.3 標準文件描述符&…

Agent實戰教程:深度解析async異步編程在Langgraph中的性能優化

在現代Python開發中&#xff0c;異步編程已經成為提高程序性能的重要手段&#xff0c;特別是在處理網絡請求、數據庫操作或AI模型調用等耗時操作時。本文將通過實際的LangGraph 示例&#xff0c;深入解析async的真正作用&#xff0c;并揭示一個常見誤區&#xff1a;為什么異步順…

coalesce在sql中什么作用

COALESCE?是SQL中的一個函數&#xff0c;用于返回參數列表中的第一個非空值&#xff0c;若所有參數均為NULL則返回NULL&#xff0c;常用于處理數據中的空值情況。 ?核心功能與語法? COALESCE函數的基本語法為&#xff1a;COALESCE(expression1, expression2, ..., express…

【Rust】 6. 字符串學習筆記

一、Rust 字符串概述 Rust 字符串是 UTF-8 編碼的文本序列&#xff0c;提供兩種主要類型&#xff1a; &str - 字符串切片&#xff08;通常作為引用出現&#xff09;String - 動態可變的、擁有所有權的字符串 二、字符串字面量 (&str) 編譯時已知大小&#xff0c;靜態分…

達夢數據庫-數據文件 (二)

達夢數據庫-數據文件&#xff08;二&#xff09;-自動監控達夢數據庫表空間使用率的 Shell 腳本 自動監控達夢數據庫表空間使用率的 Shell 腳本&#xff0c;支持&#xff1a; ? 實時計算每個表空間的使用率? 設置閾值告警&#xff08;如 >80%&#xff09;? 支持郵件告警&…

如何用 Android 平臺開發第一個 Kotlin 小程序

安裝開發環境下載并安裝最新版 Android Studio&#xff08;官方 IDE&#xff09;&#xff0c;安裝時勾選 Kotlin 插件。確保 JDK 版本為 11 或更高。創建新項目打開 Android Studio 選擇 File > New > New Project&#xff0c;選擇 Empty Activity 模板。在配置界面中&am…

Java常用工具類

異常 (Exception)。程序世界并非總是完美的&#xff0c;異常處理機制就是Java為我們提供的優雅應對錯誤的解決方案。一、為什么需要異常處理&#xff1f;—— 從現實世界說起 想象一下現實生活中的場景&#xff1a; 開車上班&#xff1a;你計劃開車去公司&#xff08;正常流程&…

AWS亞馬遜云賬號注冊指南

AWS是全球領先的云計算平臺&#xff0c;提供廣泛的云服務。賬號注冊是開端&#xff0c;不管是用來學習、搭建個人項目&#xff0c;還是公司項目部署上線需要&#xff0c;都需要進行這一步。提醒&#xff1a;在使用賬戶之前&#xff0c;必須要綁定國際的信用卡&#xff1b;通過我…

云計算學習100天-第31天

Keepalived概念keepalived 是Linux下一個輕量級的高可用解決方案主要是通過虛擬路由冗余協議(VRRP)來實現高可用功能Virtual Router Redundancy Protocol起初就是為了補充LVS功能而設計的&#xff0c;用于監控LVS集群內后端真實服務器狀態后來加入了VRRP的功能&#xff0c;它出…

2025年視覺、先進成像和計算機技術論壇(VAICT 2025)

會議簡介 作為人工智能大數據創新發展論壇的重要分論壇&#xff0c;2025年視覺、先進成像和計算機技術論壇聚焦人工智能感知世界的核心前沿&#xff0c;將于2025年9月18-20日在中國廣州廣東科學館舉行。 視覺與成像技術是智能系統理解環境的關鍵&#xff0c;計算機技術則…