01-背包,数组从后往前倒过来的思路

0-1背包参考数据

  • 第一行代表n个物品和背包容量c
  • 第二行代表每个物品的重量 w i w_i wi
  • 第三行代表每个物品的价值 v i v_i vi
4 9
2 3 4 5
2 4 5 7

计算机算法分析和设计第五版书中这样描述
m ( i , j ) = m a x ( m ( i + 1 , j ) , m ( i + 1 , j − w i ) + v i ) 当 j > = w i m(i,j) = max(m(i + 1, j), m(i + 1, j - w_i) + v_i) 当 j >=w_i m(i,j)=max(m(i+1,j),m(i+1,jwi)+vi)j>=wi
或者
m ( i , j ) = m ( i + 1 , j ) 当 0 < = j < w i m(i, j) = m(i + 1, j) 当 0 <= j < w_i m(i,j)=m(i+1,j)0<=j<wi

  • 我们假设这里有一个表格,行是n,列是c;我们从后往前填(倒思路)
  1. 开始我们选择最后一个物品,如果最后一个物品重量小于c,这里最后一个物品重量是5,小于c=9,我们设定,w[n]-1无法放入,下图黄色区域,剩余部分可以放入。
    在这里插入图片描述
  2. 我们继续从n - 1个物品开始考虑之前的物品,按照公式,我们可以把第三行表格分为两个部分,一个部分无法放入新物品,一个部分可以放入。先填入无法放入的区域,价值为m(i, j) = m(i+1, j)。即表示第三行和第四行数据相同。如下图绿色部分,之后从最右边往左开始填数字,即从9开始天道第四列,这些都可可能产生最新价值的部分 m ( i , j ) = m a x ( m ( i + 1 , j ) , m i + 1 , j − w i ) + v i m(i, j) = max(m(i+1, j), mi+1, j-w_i) + v_i m(i,j)=max(m(i+1,j),mi+1,jwi)+vi
    在这里插入图片描述
  3. 继续按照步骤,直到第一行
    在这里插入图片描述
  4. 最后一行单独考虑,不用计算全部,只需要考虑能不能放下最后一个物品,最终的表格如下,非常巧妙。
    在这里插入图片描述
  • 参考代码
#include <algorithm>
#include <iostream>

using namespace std;
int track(int v[], int w[], int c, int n, int m[][100])
{
    int Jmax = min(w[n] - 1, c);
    /*
     * 1. Jmax如果等于w[n] - 1 说明,第n件物品无法放下,前[0, w[n]-1]的重量区间可装载0
     * 2. Jmax如果为c,说明第n件物品比c还重,因此[0,c]区间重量可装载0
     */
    for (int j = 0; j <= Jmax; j++) {
        m[n][j] = 0;
    }
    // 如果可以装下第n件物品,则第n栏,该重量位置价值为v[n]
    for (int j = w[n]; j <=c; j++)
        m[n][j] = v[n];

    for (int i = n - 1; i > 1; i--) {
        Jmax = min(w[i] - 1, c);
        /*
         * 1. Jmax如果等于w[i] - 1 说明,第n件物品无法放下,前[0, w[n]-1]的重量区间可装载重量不变,和之前相同
         * 2. Jmax如果为c,说明第n件物品比c还重,因此[0,c]区间重量可装载不变,和之前相同
         */
        for (int j = 0; j <= Jmax; j++) {
            m[i][j] = m[i+1][j];
        }
        // 如果可以放下,物品的价值大小取决于max(之前大小,去除w[i]件物品可以放下的最大重量+当前w[i]重量)
        for (int j = w[i]; j <=c; j++) {
            m[i][j] = max(m[i+1][j], m[i+1][j-w[i]] + v[i]);
        }
        // 是否能放下最后一个物品
        m[1][c] = m[2][c];
        if (c >= w[1]) {
            m[1][c] = max(m[1][c], m[2][c-w[1]] + v[1]);
        }
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <=c; ++j) {
            cout << m[i][j] << " ";
        }
        cout << endl;
    }
    return m[1][c];
}
int main()
{
    int n = 4;
    int c = 9;
    int w[5] = {0, 2, 3, 4, 5};
    int v[5] = {0, 3, 4, 5, 7};
    int m[100][100] = {0};
    cout  << track(v, w, c, n, m) << endl;
    return 0;
}

结果:

0 0 0 0 0 0 0 0 12
0 0 4 5 7 7 9 11 12
0 0 0 5 7 7 7 7 12
0 0 0 0 7 7 7 7 7
12

这个和正序的有什么区别,这里列出一个表格,正序的代码简单一点,思考简单一点。第一次遇到书本写01-背包问题,发现和当初竞赛的学习以及以前的知识有点不同,因此记录了一下:
在这里插入图片描述

for(int i = 1; i <= n; i++) {
     for(int j = 0; j <= m; j++)
     {
         //  当前背包容量装不进第i个物品,则价值等于前i-1个物品
         m[i][j] = m[i - 1][j];
         // 能装,需进行决策是否选择第i个物品
         if(j>=v[i])
         m[i][j] = max(m[i][j], m[i - 1][j - v[i]] + w[i]);
     }
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值