状压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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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 可使用下述账号登录使用 用…