狀壓DP-基本框架

狀壓DP-基本框架

    • 一、狀壓DP的核心思想與適用場景
      • 1.1 問題特征
      • 1.2 核心思想
      • 1.3 與傳統DP的對比
    • 二、位運算基礎:狀壓DP的語法
    • 三、狀壓DP的基本框架
      • 3.1 步驟拆解
      • 3.2 通用代碼模板
    • 四、經典案例詳解
      • 4.1 旅行商問題(TSP)
        • 問題描述
        • 狀壓DP設計
        • 代碼實現
      • 4.2 集合覆蓋問題(最小代價覆蓋所有元素)
        • 問題描述
        • 狀壓DP設計
        • 代碼實現
    • 五、狀壓DP的優化技巧
      • 5.1 狀態剪枝
      • 5.2 位運算優化
      • 5.3 空間優化
      • 總結

狀態壓縮動態規劃(Bitmask DP)是解決“小規模集合選擇”問題的高效方法,當問題涉及對有限元素的子集進行狀態描述時,傳統DP的多維狀態會導致空間爆炸,而狀壓DP通過二進制位表示集合狀態,將高維狀態壓縮為一個整數,顯著降低空間復雜度。

一、狀壓DP的核心思想與適用場景

1.1 問題特征

狀壓DP適用于以下場景:

  • 元素數量少:通常n≤20(二進制狀態需要2^n個,n=20對應約百萬級狀態,可接受)。
  • 狀態與子集相關:問題的解依賴于“哪些元素被選中”或“元素的選擇順序”(如旅行商問題中已訪問的城市集合)。
  • 子集狀態可轉移:從一個子集狀態可通過添加/刪除元素過渡到另一個狀態(如從“選中{0,1}”到“選中{0,1,2}”)。

1.2 核心思想

  1. 狀態壓縮:用一個整數(二進制數)表示元素的選擇狀態。例如,n=3時:

    • 二進制101(十進制5)表示選中第0和第2個元素(從右往左數,低位對應下標0);
    • 二進制000表示未選中任何元素。
  2. DP狀態定義dp[mask]表示“在狀態mask下的最優解”(如最小代價、最大價值等),其中mask是壓縮后的二進制狀態。

  3. 狀態轉移:通過枚舉mask中未選中的元素,生成新狀態mask | (1 << i),并更新dp[new_mask]

1.3 與傳統DP的對比

維度傳統DP(如背包問題)狀壓DP
狀態表示多維數組(如dp[i][j]單整數mask(壓縮子集)
適用規模元素數量大(n≤1e5元素數量小(n≤20
核心技巧空間優化(如滾動數組)位運算操作(狀態轉換)
典型問題0/1背包、最長上升子序列旅行商問題、集合覆蓋問題

二、位運算基礎:狀壓DP的語法

狀壓DP依賴二進制位運算實現狀態操作,核心運算如下:

操作含義位運算表達式
檢查元素i是否選中i位是否為1(mask & (1 << i)) != 0
選中元素i將第i位設為1`mask
取消選中元素i將第i位設為0mask & ~(1 << i)
切換元素i的狀態i位0變1,1變0mask ^ (1 << i)
統計選中元素數量二進制中1的個數(popcountInteger.bitCount(mask)
清空所有元素狀態置為0mask = 0

示例:mask = 5(二進制101):

  • 檢查元素1是否選中:5 & (1<<1) = 101 & 010 = 000 → 未選中
  • 選中元素1:5 | (1<<1) = 101 | 010 = 111 → 新狀態7
  • 統計選中數量:Integer.bitCount(5) = 2

三、狀壓DP的基本框架

3.1 步驟拆解

  1. 確定狀態表示:用mask表示元素的選擇狀態,定義dp[mask]的具體含義(如代價、價值)。
  2. 初始化:設置初始狀態的dp值(如dp[0] = 0表示未選任何元素時的初始代價)。
  3. 狀態轉移
    • 遍歷所有可能的mask(從0到2^n - 1);
    • 對每個mask,枚舉可添加的元素i(未被mask選中);
    • 計算新狀態new_mask = mask | (1 << i),更新dp[new_mask]
  4. 結果提取:根據問題目標,返回dp[full_mask]full_mask = (1 << n) - 1表示所有元素都被選中)或其他特定狀態的值。

3.2 通用代碼模板

public class BitmaskDPTemplate {int n; // 元素數量int[] dp; // dp[mask]表示狀態mask下的最優解public BitmaskDPTemplate(int n) {this.n = n;int size = 1 << n; // 狀態總數:2^ndp = new int[size];// 初始化:除初始狀態外均設為無窮大(求最小值時)或負無窮(求最大值時)Arrays.fill(dp, Integer.MAX_VALUE);dp[0] = 0; // 初始狀態:未選任何元素}public int solve() {int fullMask = (1 << n) - 1; // 所有元素都被選中的狀態// 遍歷所有狀態for (int mask = 0; mask < (1 << n); mask++) {if (dp[mask] == Integer.MAX_VALUE) continue; // 跳過不可達狀態// 枚舉可添加的元素ifor (int i = 0; i < n; i++) {if ((mask & (1 << i)) != 0) continue; // 元素i已被選中,跳過int newMask = mask | (1 << i); // 選中元素i后的新狀態// 計算新狀態的代價(根據具體問題修改)int cost = calculateCost(mask, i);dp[newMask] = Math.min(dp[newMask], dp[mask] + cost);}}return dp[fullMask]; // 返回所有元素被選中時的最優解}// 根據具體問題計算從mask添加元素i的代價private int calculateCost(int mask, int i) {// 示例:代價為i+1(實際需根據問題定義)return i + 1;}public static void main(String[] args) {BitmaskDPTemplate solver = new BitmaskDPTemplate(3); // 3個元素System.out.println(solver.solve()); // 輸出0+1+2+3=6(示例邏輯)}
}

四、經典案例詳解

4.1 旅行商問題(TSP)

問題描述

給定n個城市和兩兩之間的距離,求從起點出發,訪問所有城市恰好一次并返回起點的最短路徑。

  • 示例:n=4,距離矩陣dist[i][j]表示城市ij的距離,求最短回路。
狀壓DP設計
  1. 狀態定義dp[mask][u]表示“已訪問的城市集合為mask,當前在城市u時的最短距離”。
    (注:此處狀態包含mask和當前城市u,是二維狀壓DP)

  2. 狀態轉移

    • 初始狀態:dp[1 << start][start] = 0(僅訪問起點,距離為0)。
    • 對每個mask和當前城市u,枚舉未訪問的城市v,則:
      dp[mask | (1 << v)][v] = min(dp[mask | (1 << v)][v], dp[mask][u] + dist[u][v])
  3. 結果計算:訪問所有城市后返回起點,總距離為min(dp[full_mask][u] + dist[u][start])u為最后一個城市)。

代碼實現
import java.util.*;public class TSP {int n;int[][] dist; // 距離矩陣int[][] dp; // dp[mask][u]: 狀態mask下當前在u的最短距離final int INF = 1 << 30;public TSP(int n, int[][] dist) {this.n = n;this.dist = dist;int size = 1 << n;dp = new int[size][n];// 初始化:所有狀態設為無窮大for (int i = 0; i < size; i++) {Arrays.fill(dp[i], INF);}// 起點設為0,初始狀態:僅訪問0dp[1 << 0][0] = 0;}public int shortestPath() {int fullMask = (1 << n) - 1;// 遍歷所有狀態for (int mask = 0; mask < (1 << n); mask++) {// 遍歷當前所在城市ufor (int u = 0; u < n; u++) {if ((mask & (1 << u)) == 0) continue; // u不在mask中,跳過if (dp[mask][u] == INF) continue; // 狀態不可達// 枚舉未訪問的城市vfor (int v = 0; v < n; v++) {if ((mask & (1 << v)) != 0) continue; // v已訪問,跳過int newMask = mask | (1 << v);// 更新新狀態的距離if (dp[newMask][v] > dp[mask][u] + dist[u][v]) {dp[newMask][v] = dp[mask][u] + dist[u][v];}}}}// 計算返回起點的總距離int result = INF;for (int u = 0; u < n; u++) {if (u == 0) continue; // 起點無需返回result = Math.min(result, dp[fullMask][u] + dist[u][0]);}return result;}public static void main(String[] args) {// 示例:4個城市的距離矩陣int n = 4;int[][] dist = {{0, 1, 2, 3},{1, 0, 4, 5},{2, 4, 0, 6},{3, 5, 6, 0}};TSP tsp = new TSP(n, dist);System.out.println(tsp.shortestPath()); // 輸出1+4+6+3=14(示例路徑:0→1→2→3→0)}
}

4.2 集合覆蓋問題(最小代價覆蓋所有元素)

問題描述

給定n個元素和m個子集,每個子集S_i有代價c_i,求代價最小的子集組合,使其覆蓋所有n個元素。

  • 示例:n=3,子集S_0={0,1}(代價2),S_1={1,2}(代價3),S_2={0,2}(代價4),最優解為S_0 + S_1(代價5,覆蓋{0,1,2})。
狀壓DP設計
  1. 狀態定義dp[mask]表示“覆蓋元素集合為mask時的最小代價”。

  2. 狀態轉移

    • 初始狀態:dp[0] = 0(覆蓋空集代價0)。
    • 對每個mask和每個子集S_i,新覆蓋的集合為new_mask = mask | S_i_maskS_i_mask是子集S_i的二進制表示),則:
      dp[new_mask] = min(dp[new_mask], dp[mask] + c_i)
  3. 結果提取dp[full_mask]full_mask = (1 << n) - 1)。

代碼實現
import java.util.*;public class SetCover {int n; // 元素總數int m; // 子集數量int[] subsetMask; // 每個子集的二進制表示int[] cost; // 每個子集的代價int[] dp; // dp[mask]: 覆蓋mask集合的最小代價final int INF = 1 << 30;public SetCover(int n, int m, int[] subsetMask, int[] cost) {this.n = n;this.m = m;this.subsetMask = subsetMask;this.cost = cost;int size = 1 << n;dp = new int[size];Arrays.fill(dp, INF);dp[0] = 0; // 初始狀態}public int minTotalCost() {int fullMask = (1 << n) - 1;// 遍歷所有狀態for (int mask = 0; mask < (1 << n); mask++) {if (dp[mask] == INF) continue;// 枚舉每個子集for (int i = 0; i < m; i++) {int newMask = mask | subsetMask[i];// 更新新狀態的代價if (dp[newMask] > dp[mask] + cost[i]) {dp[newMask] = dp[mask] + cost[i];}}}return dp[fullMask];}public static void main(String[] args) {int n = 3; // 元素0,1,2int m = 3; // 3個子集int[] subsetMask = {0b110, // S0: {1,2}(二進制從右數,0b110表示第1和2位為1)0b101, // S1: {0,2}0b011  // S2: {0,1}};int[] cost = {2, 3, 4};SetCover solver = new SetCover(n, m, subsetMask, cost);System.out.println(solver.minTotalCost()); // 輸出2+3=5(S0覆蓋1,2,S1覆蓋0)}
}

五、狀壓DP的優化技巧

5.1 狀態剪枝

  • 跳過不可達狀態:對dp[mask]為無窮大的狀態,直接跳過轉移(如模板中if (dp[mask] == INF) continue)。
  • 按狀態中1的數量遍歷:部分問題中,狀態轉移僅能從含k個1的mask轉移到含k+1個1的mask,可按k遞增順序遍歷,減少無效循環。

5.2 位運算優化

  • 快速枚舉子集:用submask = (submask - 1) & mask枚舉mask的所有非空子集,適用于“從子集轉移”的問題。
  • 預計算狀態信息:提前計算每個mask中1的數量(bitCount)、最低位1的位置等,避免重復計算。

5.3 空間優化

  • 滾動數組:對二維狀壓DP(如TSP的dp[mask][u]),若狀態轉移僅依賴低維mask,可嘗試壓縮空間。
  • 稀疏狀態存儲:對大部分狀態不可達的問題,用哈希表存儲dp[mask],減少內存占用。

總結

狀壓DP通過二進制位壓縮集合狀態,將“子集選擇”問題轉化為可高效求解的動態規劃問題:

  1. 狀態壓縮:用整數mask表示元素的選擇狀態,將高維狀態降為低維。
  2. 位運算操作:通過與、或、異或等運算實現狀態的檢查與轉換。
  3. 狀態轉移:從當前狀態枚舉可添加的元素,生成新狀態并更新最優解。

狀壓DP的適用范圍雖受限于元素數量(通常n≤20),但在小規模問題中表現卓越,是競賽和工程中解決集合優化問題的利器。掌握它我們需要:

  • 熟練運用位運算操作;
  • 準確設計dp[mask]的狀態含義;
  • 針對具體問題優化轉移邏輯。

That’s all, thanks for reading~~
覺得有用就點個贊、收進收藏夾吧!關注我,獲取更多干貨~

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

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

相關文章

Web 端 AI 圖像生成技術的應用與創新:虛擬背景與創意圖像合成

隨著 Stable Diffusion、Midjourney 等生成式 AI 模型的爆發,Web 端圖像生成技術從“實驗室demo”走向“工業化應用”。其中,虛擬背景替換(如視頻會議的動態背景生成)和創意圖像合成(如用戶上傳素材與 AI 生成元素的融合)成為最具代表性的場景,它們通過“文本描述→AI 生…

應急響應知識總結

應急響應 Windows系統 查賬號 1、查看服務器是否有弱口令&#xff0c;遠程管理端口是否對公網開放。 檢查方法&#xff1a;據實際情況咨詢相關服務器管理員。 2、查看服務器是否存在可疑賬號、新增賬號。 檢查方法&#xff1a;打開 cmd 窗口&#xff0c;輸入 lusrmgr.msc …

智慧水務賦能二次供水管理精細化轉型:物聯網驅動的全鏈路解決方案

隨著我國城鎮化率激增&#xff0c;高層建筑占比上升&#xff0c;二次供水系統已成為保障城市供水安全的核心環節。然而&#xff0c;傳統管理模式面臨設備老化、運維粗放、監管缺失等矛盾&#xff0c;在此背景下&#xff0c;《“十四五”節水型社會建設規劃》明確要求推進二次供…

tsmc 5nm lvs之 short難搞的類型

1、M3層以上的層次發生的short&#xff0c;dengsity很高的情況下&#xff0c;兩根信號net導致的short&#xff0c;刪除其中一根然后ecoRoute fix不掉的情況下&#xff0c;該怎么辦&#xff0c;可以嘗試去cut 周圍或者上方的power。 2、M1&#xff0c; M2由于cell 內部出pin&…

初識神經網絡01——認識PyTorch

文章目錄一、認識PyTorch1.1 PyTorch是什么1.2 安裝PyTorch二、認識Tensor2.1 創建Tensor2.1.1 基本方式2.2.2 創建線性和隨機張量2.2 Tensor屬性2.2.1 切換設備2.2.2 類型轉換2.3 Tensor與Numpy的數據轉換2.3.1 張量轉ndarray2.3.2 Numpy轉張量2.4 Tensor常見操作2.4.1 取值2.…

Android UI 組件系列(十一):RecyclerView 多類型布局與數據刷新實戰

博客專欄&#xff1a;Android初級入門UI組件與布局 源碼&#xff1a;通過網盤分享的文件&#xff1a;Android入門布局及UI相關案例 鏈接: https://pan.baidu.com/s/1EOuDUKJndMISolieFSvXXg?pwd4k9n 提取碼: 4k9n 引言 在 Android 應用中&#xff0c;RecyclerView 是最常用…

如何學習跨模態對齊(尤其是 CLIP 思想)

學習跨模態對齊&#xff08;尤其是CLIP思想&#xff09;需要結合理論基礎、經典模型原理、實踐復現和前沿擴展&#xff0c;以下是一套系統的學習路徑&#xff0c;從入門到深入逐步展開&#xff1a; 一、先補基礎&#xff1a;跨模態對齊的“前置知識” 跨模態對齊的核心是讓圖…

日記研究:一種深入了解用戶真實體驗的UX研究方法

在用戶體驗&#xff08;UX&#xff09;研究中&#xff0c;我們常常需要了解用戶在真實世界中如何與產品互動。然而&#xff0c;由于時間和空間的限制&#xff0c;我們很難像“特工”一樣全天候跟蹤用戶。這時&#xff0c;“日記研究”&#xff08;Diary Studies&#xff09;就成…

鴻蒙app 開發中 加載圖片的時候閃一下 如何解決

1.解決 在圖片上 加載這個屬性 .syncLoad(true) 參考的官方鏈接

【OS】進程與線程

進程進程實體代碼段相關數據PCB進程標識符外部標識符&#xff1a;為方便用戶對進程的訪問&#xff0c;為每個進程設置一個外部標識符&#xff0c;通常由字母和數字組成內部標識符&#xff1a;為方便系統對進程的使用&#xff0c;在OS中又為進程設置了內部標識符&#xff0c;賦予…

Django 序列化詳解:從 Model 到 JSON,全面掌握數據轉換機制

一、引言&#xff1a;什么是 Django 序列化&#xff1f;在 Web 開發中&#xff0c;序列化&#xff08;Serialization&#xff09; 是指將復雜的數據結構&#xff08;如數據庫模型對象&#xff09;轉換為可傳輸的格式&#xff08;如 JSON、XML、YAML 等&#xff09;&#xff0c;…

茶葉蛋大冒險小游戲流量主微信抖音小程序開源

游戲特點 響應式設計&#xff1a;完美適配各種移動設備屏幕尺寸 直觀的觸摸控制&#xff1a;左右滑動屏幕控制茶葉蛋移動 中式風格元素&#xff1a; 茶葉蛋角色帶有裂紋紋理和可愛表情 筷子、蒸籠等中式廚房元素作為障礙物 八角、茶葉等香料作為收集物 鍋底火焰動畫效果 游戲機…

區分郵科工業交換機與路由器

在這個數字化的時代&#xff0c;我們每天都在享受著互聯網帶來的便利。無論是工作還是娛樂&#xff0c;網絡已經成為我們生活中不可或缺的一部分。然而&#xff0c;在這個看似簡單的背后&#xff0c;隱藏著兩個至關重要的設備——郵科工業交換機和路由器。它們就像網絡世界的雙…

【數據結構入門】數組和鏈表的OJ題(2)

目錄 1.回文鏈表 分析&#xff1a; 代碼&#xff1a; 2.相交鏈表 分析&#xff1a; 代碼&#xff1a; 3.環形鏈表 分析&#xff1a; 代碼&#xff1a; 面試提問&#xff1a; 4.環形鏈表II 分析1&#xff1a; 分析2&#xff1a; 代碼&#xff1a; 5.隨機鏈表的復…

文件包含篇

web78 第一題filter偽協議直接讀源碼即可 ?filephp://filter/convert.base64-encode/resourceflag.php web79 flag.php的php無法用大小寫繞過&#xff0c;所以用Php://input只讀流 import requests url "http://fadb524a-f22d-4747-a35c-82f71e84bba7.challenge.ctf.sho…

互作蛋白組學技術對比:鄰近標記與傳統IP-MS、Pull down-MS優勢對比

在生命科學領域&#xff0c;蛋白質間的相互作用構成了生命活動的核心網絡&#xff0c;驅動著信號傳導、基因調控、代謝途徑等關鍵過程。為了繪制這幅復雜的“分子互作地圖”&#xff0c;科學家們開發了多種技術&#xff0c;其中免疫共沉淀結合質譜&#xff08;IP-MS&#xff09…

(ZipList入門筆記一)ZipList的節點介紹

ZipList是 Redis 中一種非常緊湊、節省內存的數據結構 Ziplist&#xff08;壓縮列表&#xff09; 的內部內存布局。它被用于存儲元素較少的 List、Hash 和 Zset。 下面我們來詳細介紹每一個節點的含義&#xff1a; 1. zlbytes (ziplist bytes) 含義&#xff1a; 整個壓縮列…

Unix 發展史概覽

這里是一個簡明清晰的 Unix 發展史概覽&#xff0c;涵蓋從起源到現代的重要節點和演變過程。Unix 發展史概覽 1. Unix 起源&#xff08;1969年&#xff09; 貝爾實驗室&#xff1a;Ken Thompson 和 Dennis Ritchie 開發出 Unix 操作系統。最初設計目標&#xff1a;簡潔、可移植…

基于coze studio開源框架二次定制開發教程

目錄 一、 項目介紹 1.1 什么是Coze Studio 1.2 功能清單 1.3對比商業版本 二、 功能定開說明 2.1 技術棧簡介 2.2 項目架構

RHCE認證題解

考前說明請勿更改 IP 地址。DNS 解析完整主機名&#xff0c;同時也解析短名稱。? 所有系統的 root 密碼都是 redhat? Ansible 控制節點上已創建用戶賬戶 devops。可以使用 ssh 訪問? 所需的所有鏡像保存在鏡像倉庫 utility.lab.example.compodman 可使用下述賬號登錄使用 用…