《C++算法竞赛攻略:从入门到降维打击》——第13章

第13章:背包问题 (Knapsack Problem)

背包问题是动态规划中的一类经典问题,也是各类机试和竞赛中的常客。它描述了在给定容量的背包和一组具有价值和重量(或体积、成本)的物品时,如何选择物品放入背包以使得总价值最大化。背包问题的变种极多,但其核心思想和解法范式是相通的。掌握了背包问题,不仅能解决一系列相关题目,更能加深对动态规划“状态”与“选择”精髓的理解。

本章将重点介绍三种最核心的背包模型:0-1背包、完全背包和多重背包,并详细讲解如何通过状态压缩(滚动数组)技巧来优化空间复杂度。

13.1 0-1背包

问题描述

这是最简单的背包模型。我们有 NNN 件物品和一个容量为 VVV 的背包。第 iii 件物品的成本(或重量)是 wiw_iwi,价值是 viv_ivi每件物品只有一件,你可以选择将它放入背包,也可以选择不放。求解将哪些物品装入背包,可使这些物品的成本总和不超过背包容量,且价值总和最大。

核心思想与DP分析

0-1背包的“0-1”正体现在每件物品只有两种选择:不放(0)或放(1)。这是典型的动态规划应用场景。

  1. 状态表示 (dp 数组的含义):
    我们定义一个二维数组 dp[i][j]。它的含义是:从前 iii 件物品中任意选择,放入一个容量为 jjj 的背包中所能获得的最大价值。我们的最终目标就是求 dp[N][V]

  2. 状态转移方程:
    当我们面对第 iii 件物品时,需要决定是否将其放入容量为 jjj 的背包中。此时,我们有两种决策:

    • 不放第 iii 件物品:如果我们不放第 iii 件物品,那么问题就转化为:用前 i−1i-1i1 件物品来填满容量为 jjj 的背包。此时的最大价值就是 dp[i-1][j]
    • 放第 iii 件物品:要放下第 iii 件物品,前提是背包的当前容量 jjj 必须大于等于物品 iii 的成本 wiw_iwi。如果满足这个条件,我们获得的价值就是物品 iii 本身的价值 viv_ivi,加上用前 i−1i-1i1 件物品来填满剩余容量 j−wij - w_ijwi 的背包所能获得的最大价值,即 v_i + dp[i-1][j - w_i]

    既然要求最大价值,我们自然是在这两种决策中选择更优的一个。因此,状态转移方程如下:

    dp[i][j]=max⁡(dp[i−1][j],dp[i−1][j−wi]+vi)(j≥wi)dp[i][j] = \max(dp[i-1][j], \quad dp[i-1][j - w_i] + v_i) \quad (j \ge w_i)dp[i][j]=max(dp[i1][j],dp[i1][jwi]+vi)(jwi)

  3. 初始化:
    当没有物品可选 (i=0i=0i=0) 或者背包容量为0 (j=0j=0j=0) 时,最大价值显然为0。因此,可以将整个 dp 数组初始化为0。

C++实现 (二维数组)

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int MAXN = 101;
const int MAXV = 1001;

int w[MAXN]; // 物品的成本
int v[MAXN]; // 物品的价值
int dp[MAXN][MAXV]; // dp[i][j]表示前i个物品放入容量为j的背包的最大价值

int main() {
   
   
    int N, V;
    cin >> N >> V; // 物品数量和背包容量

    for (int i = 1; i <= N; ++i) {
   
   
        cin >> w[i] >> v[i];
    }

    // 初始化dp数组,在C++中全局数组默认初始化为0,这里可以省略
    // for (int i = 0; i <= N; ++i) {
   
   
    //     for (int j = 0; j <= V; ++j) {
   
   
    //         dp[i][j] = 0;
    //     }
    // }

    // 开始动态规划
    for (int i = 1; i <= N; ++i) {
   
   
        for (int j = 0; j <= V; ++j) {
   
   
            // 首先,不放第i件物品
            dp[i][j
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱看烟花的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值