美文网首页
0-1 背包问题详解

0-1 背包问题详解

作者: 微糖去冰_ | 来源:发表于2019-10-06 10:02 被阅读0次

    给定一组有固定价值和固定重量的物品,以及一个已知最大承重量的背包,求在不超过背包最大承重量的前提下,能放进背包里面的物品的最大总价值。

    这一类问题是典型的使用动态规划解决的问题,我们可以把背包问题分成3种不同的子问题:0-1背包问题、完全背包和多重背包问题。下面对这三种问题分别进行讨论。

    1.0-1背包问题

    0-1背包问题是指每一种物品都只有一件,可以选择放或者不放。现在假设有n件物品,背包承重为m。

    对于这种问题,我们可以采用一个二维数组去解决:f[i][j],其中i代表加入背包的是前i件物品,j表示背包的承重,f[i][j]表示当前状态下能放进背包里面的物品的最大总价值。那么,f[n][m]就是我们的最终结果了。

    采用动态规划,必须要知道初始状态和状态转移方程。初始状态很容易就能知道,那么状态转移方程如何求呢?对于一件物品,我们有放进或者不放进背包两种选择:

    (1)假如我们放进背包,f[i][j] = f[i - 1][j - weight[i]] + value[i],这里的f[i - 1][j - weight[i]] + value[i]应该这么理解:在没放这件物品之前的状态值加上要放进去这件物品的价值。而对于f[i - 1][j - weight[i]]这部分,i - 1很容易理解,关键是 j - weight[i]这里,我们要明白:要把这件物品放进背包,就得在背包里面预留这一部分空间。
    (2)假如我们不放进背包,f[i][j] = f[i - 1][j],这个很容易理解。因此,我们的状态转移方程就是:f[i][j] = max(f[i][j] = f[i - 1][j] , f[i - 1][j - weight[i]] + value[i]) 。当然,还有一种特殊的情况,就是背包放不下当前这一件物品,这种情况下f[i][j] = f[i - 1][j]。

    
    #include <iostream>
    #define V 500
    using namespace std;
    int weight[20 + 1];
    int value[20 + 1];
    int f[V + 1];
    int main() {
        int n, m;
        cout << "请输入物品个数:";
        cin >> n;
        cout << "请分别输入" << n << "个物品的重量和价值:" << endl; 
        for (int i = 1; i <= n; i++) {
            cin >> weight[i] >> value[i];
        }
        cout << "请输入背包容量:";
        cin >> m;
        for (int i = 1; i <= n; i++) {
            for (int j = m; j >= 1; j--) {
                if (weight[i] <= j) {
                    f[j] = f[j] > f[j - weight[i]] + value[i] ? f[j] : f[j - weight[i]] + value[i];
                }
            }
        }
        cout << "背包能放的最大价值为:" << f[m] << endl;
    }
    

    题目描述

    这个表格对于理解0-1背包问题很有用,我们利用它来理解一下为什么要把第二层循环颠倒这个问题。考虑d9这一项,要求出这个状态,我们有可能利用到的就是e1到e8这8个状态,当我们把第二层循环颠倒过来时,当我们要求f[j]时,f[j -1]到f[1]还保存着下面一行的状态,因此可以采用一维数组解决。我们可以考虑一下假如不把第二层循环颠倒,当要求f[j]时,f[j - 1]到f[1]已经是同一行的状态了,根本没法求。

    for (int i = 1; i <= n; i++) {
         for (int j = m; j >= 1; j--) {
             if (weight[i] <= j) {
                 f[j] = f[j] > f[j - weight[i]] + value[i] ? f[j] : f[j - weight[i]] + value[i];
             }
         }
    }
    

    2.完全背包问题

    #include <iostream>
    #define V 500
    using namespace std;
    int weight[20 + 1];
    int value[20 + 1];
    int f[V + 1];
    int max(int a, int b) {
        return a > b ? a : b;
    }
    int main() {
        int n, m;
        cout << "请输入物品个数:";
        cin >> n;
        cout << "请分别输入" << n << "个物品的重量和价值:" << endl; 
        for (int i = 1; i <= n; i++) {
            cin >> weight[i] >> value[i];
        }
        cout << "请输入背包容量:";
        cin >> m;
        for (int i = 1; i <= n; i++) {
            for (int j = weight[i]; j <= m; j++) {
                f[j] = max(f[j], f[j - weight[i]] + value[i]);
            }
        }
        cout << "背包能放的最大价值为:" << f[m] << endl;
    }
    

    完全背包问题与0-1背包问题的区别
    1.完全背包问题内层循环是顺序的,而0-1背包问题是逆序的。。
    0-1背包为逆序的原因是为了求max中的两项是前一状态的值。
    而现在是要求当前状态的值,因为每种背包都是无限的,当我们从1到N循环时,f[v]表示容量为v在前i种背包所得的价值,这里我们添加的不是前一个背包,而且当前背包,所以应考虑当前状态。

    3.多重背包问题

    多重背包问题限定了一种物品的个数,解决多重背包问题,只需要把它转化为0-1背包问题即可。比如,有2件价值为5,重量为2的同一物品,我们就可以分为物品a和物品b,a和b的价值都为5,重量都为2,但我们把它们视作不同的物品。

    #include <iostream>
    using namespace std;
    #define V 1000
    int weight[50 + 1];
    int value[50 + 1];
    int num[20 + 1];
    int f[V + 1];
    int max(int a, int b) {
        return a > b ? a : b;
    }
    int main() {
        int n, m;
        cout << "请输入物品个数:";
        cin >> n;
        cout << "请分别输入" << n << "个物品的重量、价值和数量:" << endl; 
        for (int i = 1; i <= n; i++) {
            cin >> weight[i] >> value[i] >> num[i];
        }
        int k = n + 1;
        for (int i = 1; i <= n; i++) {
            while (num[i] != 1) {
                weight[k] = weight[i];
                value[k] = value[i];
                k++;
                num[i]--;
            }
        }
        cout << "请输入背包容量:";
        cin >> m;
        for (int i = 1; i <= k; i++) {
            for (int j = m; j >= 1; j--) {
                if (weight[i] <= j) f[j] = max(f[j], f[j - weight[i]] + value[i]);
            }
        }
        cout << "背包能放的最大价值为:" << f[m] << endl;
    }
    

    相关文章

      网友评论

          本文标题:0-1 背包问题详解

          本文链接:https://www.haomeiwen.com/subject/isjlyctx.html