当前位置:首页 > 技术 > 正文内容

01背包问题的动态规划解法详解

访客 技术 2026年9月24日 12

问题描述

给定 n 件物品和容量为 V 的背包。第 i 件物品的重量是 w[i],价值是 v[i]。求解将哪些物品装入背包,可使这些物品的总重量不超过背包容量,且总价值最大。

问题分析

对于这类优化问题,我们首先考虑穷举法。使用深度优先搜索或广度优先搜索确实能得到解,但时间复杂度达到指数级 O(2^n),对于 n=1000 的数据规模完全不可行。贪心算法虽然效率高,但往往只能得到近似解,无法保证最优性。因此,动态规划成为解决此类问题的理想选择。

动态规划适用场景

  • 问题可以通过递归或搜索思路描述,但直接求解效率低下
  • 求解有限集合的极值问题(最大值、最小值等)
  • 问题状态可以用数字组合表示,如本例中的 f(i, j)

动态规划解题步骤

第一步:状态定义

定义 dp[i][j] 表示从前 i 件物品中选择,在背包容量不超过 j 的情况下能获得的最大价值。这个状态包含两个维度:物品数量和剩余容量。

第二步:状态转移方程

对于第 i 件物品,有两种选择:

  • 不选第 i 件物品:dp[i][j] = dp[i-1][j]
  • 选第 i 件物品(前提是容量足够):dp[i][j] = dp[i-1][j-w[i]] + v[i]

综合两种选择,状态转移方程为:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]) (当 j ≥ w[i] 时)

基础实现代码

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 1005;

int dp[MAXN][MAXN];
int weight[MAXN], value[MAXN];

int main() {
    int n, capacity;
    cin >> n >> capacity;
    
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i];
    }
    
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= capacity; j++) {
            dp[i][j] = dp[i-1][j];
            if (j >= weight[i]) {
                dp[i][j] = max(dp[i][j], dp[i-1][j-weight[i]] + value[i]);
            }
        }
    }
    
    cout << dp[n][capacity] << endl;
    return 0;
}

空间优化

观察状态转移方程可以发现,计算第 i 行时只依赖第 i-1 行的数据。因此可以使用滚动数组技术,将二维数组压缩为一维数组,将空间复杂度从 O(nV) 降到 O(V)。

需要注意的是,为了避免重复使用同一件物品,内层循环需要从后向前遍历容量。

优化后实现代码

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 1005;

int dp[MAXN];
int weight[MAXN], value[MAXN];

int main() {
    int n, capacity;
    cin >> n >> capacity;
    
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i];
    }
    
    for (int i = 1; i <= n; i++) {
        for (int j = capacity; j >= weight[i]; j--) {
            dp[j] = max(dp[j], dp[j-weight[i]] + value[i]);
        }
    }
    
    cout << dp[capacity] << endl;
    return 0;
}

通过空间优化,我们将二维动态规划转化为高效的一维实现,既保持了正确性,又显著降低了内存占用。这种优化技巧在背包问题中非常常用,值得深入理解。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。