动态规划-01背包问题

  1. 问题描述

有N件物品和一个容量是V的背包,每件物品只能使用一次,第i件物品的体积是vi,价值是wi,求解将哪些物品装入背包可以使物品的总体积不超过背包容量,且总价值最大,输出最大价值。

  1. 输入格式及输出格式

输入格式:第一行两个整数,表示物品数量和背包容积,接下来有N行,没行两个整数vi和wi,用空格分开,分别表示第i件物品的体积和价值。

输出格式:输出一个整数,表示最大价值

  1. 基本思想

首先,我们可以设置一个f[i][j]用来表示,物品遍历到第i个时,容量为j的最大价值(物品可能放了也可能会没放),设置两重循环,第一层用来遍历物品,第二层遍历容量,将每一层的f[i][j]初始化赋值为f[i-1][j],表示初始化的情况为不拿这一层的东西。当j大于v[i]时,说明当前容量可以取得这个物品,此时,我们就需要与不拿的情况做比较取最大值,于是可得基本的动态规划方程:f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i] + w[j])

  1. 代码实现

由上述分析,可得代码如下

#include <iostream>

using namespace std;

const int N = 10010;
int n, m;
int v[N], w[N];
int f[N][N];

int main(){
    
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
    
    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ ) {
            f[i][j] = f[i - 1][j];
            if (j >= v[i]) f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
        }
    
    cout << f[n][m] << endl;
    
    return 0;
}
  1. 代码优化

细心的朋友可能会发现,我们每一次的计算,只是在前一层的基础上进行覆盖,你现在要计算f[i][j]的值,实际上可以用f[i - 1][j]暂存,可以省下一部分空间,所以第一时间我们会想到,优化代码为:

    for (int i = 1; i <= n; i ++ )
        for (int j = v[i]; j <= m; j ++ )
            f[j] = max(f[j], f[j - v[i]] + w[i]);

以上代码真的正确吗?当然不是,当我们选择以上形式的话,f[j - v[i]]其实相当于f[i][j - v[i]],这显然是与f[i - 1][j - v[i]]是不符的,换句话说,我们在动态规划的过程中,改变了我们这一层滑动数组的值,如何避免?我们可以选择从前往后进行遍历优化,即:从j容量开始,一直减到v[i],于是,我们可以得到以下的代码:

#include <iostream>

using namespace std;

const int N = 10010;
int n, m;
int v[N], w[N];
int f[N];

int main(){
    
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
    
    for (int i = 1; i <= n; i ++ )
        for (int j = m; j >= v[i]; j -- )
            f[j] = max(f[j], f[j - v[i]] + w[i]);
    
    cout << f[m] << endl;
    
    return 0;
}

任何错误,欢迎指正,Thanks♪(・ω・)ノ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值