【动态规划】01背包问题

20 篇文章 0 订阅
1 篇文章 0 订阅

01背包问题


问题描述

给定 N 种物品和一个最大载重量为 C 的背包,物品 i 的重量是 wi,其价值为 vi 。
问:应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?

问题分析

对于每个物品,只能选择装或者不装,不能选择只装物体的一部分,因此不能使用单位重量的价值进行排序的方法(贪心)来解决,需要用到动态规划来解决。


动态规划的三个核心

  1. 最优子结构
  2. 边界
  3. 状态转移方程

对于该问题而言,由于第i+1件物品只有两种选择(选与不选),因此前i+1件产品的最优解就是,子结构就是前i件物品装在承重为j的容器中,可以用result[i][j]来表示。

边界就是在只装第一件物品时的情况。

通过上面的对子结构的分析,可以得到状态转移方程:
1. j < w[i]时,即剩余载重量不足以装下当前物品,应有最优解即是前i-1件时的解,result[i][j] = result[i-1][j]
2. j >= w[i]时,即还可以装下当前物品,因此解应为 装与不装 当前物品两种情况中的最大解。如果不装,即result[i-1][j];如果装,,即result[i-1][j-w[i]] + v[i]。因此取最大值结果为result[i][j] = max( result[i-1][j], result[i-1][j-w[i]] + v[i] )


代码

1.二维数组

int getLargestValue_1( vector<int> & v, vector<int> & w, int c ) {
    vector< vector<int> > res( N+1, vector<int>( c+1, 0 ) );
    /*  该处初始化边界可以省略
        由于开始的一列为0,不影响结果  */
    /*for( int i = 1; i <= c; i++ ) {
        if( i < w[i] ) {
            res[1][i] = 0;
        } else {
            res[1][i] = v[1];
        }
    }*/

    for( int i = 1; i <= N; i++ ) {  //遍历所有物品
        for( int j = 1; j <= c; j++ ) {  //遍历容器容量
            if( j < w[i] ) {  //放不下
                res[i][j] = res[i-1][j];
            } else {  //放的下
                res[i][j] = max( res[i-1][j], res[i-1][j-w[i]] + v[i] );
            }
        }

        /* 输出遍历每件物品的结果 */
        /* for( int j = 1; j <= c; j++ ) {
            cout << res[i][j] << ' ';
        }
        cout << endl; */
    }

    return res[N][c];
}

2.两个一维数组

由于每次遍历之用到了当前行与上一行,因此可优化空间到两个一维数组。

int getLargestValue_2( vector<int> & v, vector<int> & w, int c ) {
    vector<int> preResult( c+1, 0 );  //存储上次得到的结果,对应上一行
    vector<int> result( c+1, 0 );  //存储当前行结果

    for( int i = 1; i <= N; i++ ) {
        for( int j = 1; j <= c; j++ ) {
            if( j < w[i] ) {
                result[j] = preResult[j];
            } else {
                result[j] = max( preResult[j], preResult[j-w[i]] + v[i] );
            }
        }
        for( int j = 1; j <= c; j++ ) {
            cout << preResult[j] << ' ';
        }
        cout << endl;
        preResult = result;
    }

    return result[c];
}

3. 一维数组

我们还可以发现每次使用的都是上一行当前列元素上面与左面的元素(preResult[j], preResult[j-w[i]] ),因此我们可以将每行的遍历顺序改变,即从右向左计算,即可将每次的计算结果直接覆盖到当前位置。

int getLargestValue_3( vector<int> & v, vector<int> & w, int c ) {
    vector<int> result( c+1, 0 );

    for( int i = 1; i <= N; i++ ) {
        for( int j = c; j > 0; j-- ) {  //从右向左计算
            if( j >= w[i] ) {
                result[j] = max( result[j], result[j-w[i]] + v[i] );
            }
        }
    }
    return result[c];
}

可以使用以下代码测试:

int main() {
    vector<int> v{0, 8, 10, 6, 3, 7, 2};  //价值
    vector<int> w{0, 4, 6, 2, 2, 5, 1};  //重量
    int c = 12;  //最大承载重量

    cout << getLargestValue_3( v, w, c ) << endl;

    return 0;
}

最终答案为24。

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划01背包问题是一种经典的组合优化问题。在这个问题中,给定一组物品,每个物品具有自己的重量和价值,并且有一个背包的容量上限。目标是选择一些物品放入背包中,使得背包中物品的总价值最大,同时不能超过背包的容量上限。这个问题的名称来源于每个物品只能选择装入背包一次(01)的约束条件。 解决动态规划01背包问题通常使用一个二维数组dp来表示状态。其中dp[i][j]表示在前i个物品中,在背包容量为j的情况下,背包中物品的最大总价值。通过分析,我们可以得到状态转移方程: 1. 若第i个物品不放入背包,则dp[i][j] = dp[i-1][j]; 2. 若第i个物品放入背包,则dp[i][j] = dp[i-1][j-w[i]] + v[i],其中w[i]表示第i个物品的重量,v[i]表示第i个物品的价值。 最终求解的目标是dp[N][W],即在N件物品中,背包容量为W的情况下,背包中物品的最大总价值。通过填充dp数组,可以逐步求解得到最优解。 需要注意的是,这个问题的解法是基于动态规划的思想,它的时间复杂度为O(NW),其中N表示物品的个数,W表示背包的容量。这种解法在物品个数较小、背包容量较大时效果较好。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [动态规划背包问题之01背包详解](https://blog.csdn.net/weixin_53051813/article/details/125815935)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [动态规划01背包问题](https://blog.csdn.net/CY2333333/article/details/117621356)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值