【刷题日记】0-1背包问题

该文介绍了一个背包问题的动态规划解决方案,通过遍历物品和背包容量,使用二维数组记录最大价值。有两种实现方式,一是完整二维数组,二是仅用一维数组。两种方法的时间复杂度均为O(N²),但空间复杂度不同。最后给出了相应的C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 问题描述

1.1 题目:

有 N 件物品和一个最多能背重量为 V 的背包。第i件物品的重体积是v[i],得到的价值是w[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。输出最大价值。

1.2 输出格式

输出一个整数,表示最大价值。

1.3 时/空限制

1s / 64MB

1.4 数据范围

0<N, V≤1000
0<vi, wi≤1000

2. 示例

输入:

4 5
1 2
2 4
3 4
4 5

输出: 8

3. 题解:动态规划

3.1 二维数组

遍历每个物品以及每个背包容量值的最大价值,输出最大价值即可。

i 表示当前遍历第 i 个物品, j表示当前背包最大容量,故当前背包最大值F[i][j]有两种情况

  1. 若当前物品体积wi < j, 表示当前最大容量都不够存放,故当前最大价值等于遍历前一个物品最大容量为 j 时的价值,即F[i][j] = F[i - 1][j];
  2. 若当前物品体积wi >= j, 表示当前最大容量可以存放,那么此时有两种情况即放或者不放,选择价值较大的即可。不放时价值同1,存放时F[i][j] = F[i - 1][j - v[i]] + w[i];故F[i][j] = max(F[i - 1][j], F[i - 1][j - v[i]] + w[i])

3.1.1 初始化条件

由于递推公式 F[i][j] = max(F[i - 1][j], F[i - 1][j - v[i]] + w[i]) 出现了 i - 1,故需考虑初始化值

  1. 若F[0][j]代表第0个物品,则直接初始化为0即可;
  2. 若F[0][j]代表第1个物品,则需对其进行初始化,当j小于v0式值初始化为0,其余初始化为v0

综合考虑1比较合算;

3.1.2 复杂度估计

  1. 两层遍历,时间复杂度O(N²)
    1001 * 1001 = 10^6, 1s计算量约10^7 ~ 10^8;
  2. 二维数组,空间复杂度O(N²)
    1001 * 1001 * sizeof(int) = 4 * 10^ 6 B ≈ 4M < 64 M;

3.1.3 完整代码

# include <iostream>
using namespace std;

int F[1001][1001];
int main()
{
    int N, V;
    int vol[1001], wor[1001];
    
    cin >> N >> V;
    for(int i = 1; i <= N; i++)
        cin >> vol[i] >> wor[i];
        
    for(int i = 1; i <= N; i++)
        for (int j = 1; j <= V; j++)
        {
            if (j < vol[i])
                F[i][j] = F[i - 1][j];
            else
                F[i][j] = max(F[i - 1][j], F[i - 1][j - vol[i]] + wor[i]);
        }
        
    cout << F[N][V];
    return 0;
}

3.2 二维数组

分析可知,每一次的状态仅与上一个 i 相关, 即F[i][j] 只与 f[i - 1]这一行有关系, 故只需要开辟一行数组即可, F[j]用来存放不同i时的最大值。
第 i 个物体的价值有两种情况:
如果此时背包容量不足(j > vi),F[j] = F[j];
否则, F[j] = max(F[j], F[j - v[i])
由于递推公式与 j 前的状态有关,若正序遍历会导致F[j - v[i]被更新并不是上一个 物品对应的最大价值, 故需要倒叙遍历

3.2.1 复杂度估计

  1. 两层遍历,时间复杂度O(N²)
    1001 * 1001 = 10^6, 1s计算量约10^7 ~ 10^8;
  2. 一维数组,空间复杂度O(N)
    1001 * sizeof(int) = 4 * 10^ 6 B ≈ 4KB < 64 M;

3.2.2 完整代码

# include <iostream>
using namespace std;
int f[1001];
int main()
{
    int N, V;
    int vol[1001], wor[1001];
    
    cin >> N >> V;
    for(int i = 1; i <= N; i++)
        cin >> vol[i] >> wor[i];
        
    for (int i = 1; i <= N; i++)
        for(int j = V; j >= vol[i]; j--)
            f[j] = max(f[j], f[j - vol[i]] + wor[i]);
    
    cout << f[V] << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值