zoukankan      html  css  js  c++  java
  • [算法笔记] 背包问题总结

    1. 0-1背包问题

    给定背包容量 C ,物品体积 volume[n],物品价值 value[n]每个物品只有一个

    求:在背包容量允许的情况下,装入背包物品的最大价值。

    状态定义:

    dp[i][j] 表示前 i 件物品放入容量为 j 的背包的最大价值。
    

    边界条件:

    [dp[0,j] = 0, quad 0 le j le C \ dp[i,0] = 0, quad 0 le i le n ]

    转移方程:

    [dp[i,j] = max(dp[i-1,j], dp[i-1,j-volume[i]]+value[i]) ]

    一维数组优化(必须要逆序枚举)为什么不能正序循环呢?

    在上述二维形式的状态转移方程中,很明显,(dp[i,j]) 依赖于数组的“上一行”。

    在一维形式中,如果是正序,即 j=1...bag, 如果 dp[5]被更新, dp[9]可能需要max(dp[9],dp[5]), 但此dp[5]不是二维数组形式的“上一行”,而是二维形式的“当前行”。

    代码:

    #include <iostream>
    #include <vector>
    using namespace std;
    const int n = 5, bag = 8;
    int value[] = {0, 4, 5, 2, 1, 3};
    int volume[] = {0, 3, 5, 1, 2, 2};
    int solve()
    {
        vector<int> dp(bag + 1, 0);
        for (int i = 1; i <= n; i++)
        {
            for (int j = bag; j >= 1; j--)
                if (j >= volume[i])
                    dp[j] = max(dp[j], dp[j - volume[i]] + value[i]);
        }
        for (int x : dp)
            cout << x << ' ';
        return dp[bag];
    }
    int main()
    {
        cout << solve() << endl;
    }
    

    2. 完全背包

    (n) 种物品,每种物品单件质量为 w[i],价值为 c[i],现有容量为 C 的背包,假设每种物品都有无穷件,如何选取物品,使得装入背包的价值最大。

    状态定义:

    dp[i][j]表示前 i 件物品放入容量为 j 的背包的最大价值。
    

    边界条件:

    [dp[i,0] = 0, quad 0 le i le n \ dp[0,j] = 0, quad 0 le j le C ]

    状态转移方程:

    [egin{aligned} dp[i,j] &= dp[i-1,j] quad &if quad j<w[i] \ dp[i,j] &= max(dp[i-1,j], dp[i][j-w[i]]+c[i]) &if quad j ge w[i] end{aligned} ]

    状态转移方程的含义:依据选择一个物品 (i) 放入来讨论,如果 ​(j<w[i]) 说明背包容量小于物品 ​(i) 的体积。如果 ​(j ge w[i]) ,可以分为以下 2 种情况:

    • 不放入物品 (i),那么 (dp[i,j] = dp[i-1,j])
    • 放入物品 (i),那么 (dp[i,j] = dp[i,j-w[i]]+c[i])

    (dp[i,j-w[i]]+c[i]) 实际上是表示:无论背包中有没有物品 (i),我们都只考虑当前情况下,是否放入物品 (i)

    而在 01 背包问题中,(dp[i,j] = max(dp[i-1,j], dp[i-1,j-volume[i]]+value[i]))(dp[i-1,j-volume[i]]+value[i]) 表示在前 (i-1) 的物品已经选好的情况下,是否放入物品 (i)

    二维数组写法:

    const int n = 4, bag = 10;
    int val[n + 1] = {0, 1, 3, 5, 9};
    int vol[n + 1] = {0, 2, 3, 4, 7};
    int completeKnapsack1()
    {
        vector<vector<int>> dp(n + 1, vector<int>(bag + 1, 0));
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= bag; j++)
            {
                if (j >= vol[i])
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - vol[i]] + val[i]);
                else
                    dp[i][j] = dp[i - 1][j];
            }
        }
        return dp.back().back();
    }
    

    一维数组优化,cpp实现:

    const int n = 4, bag = 10;
    int val[n + 1] = {0, 1, 3, 5, 9};
    int vol[n + 1] = {0, 2, 3, 4, 7};
    int completeKnapsack()
    {
        vector<int> dp(bag + 1, 0);
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= bag; j++)
            {
                if (j >= vol[i])
                    dp[j] = max(dp[j], dp[j - vol[i]] + val[i]);
            }
        }
        return dp.back();
    }
    

    实际上,内层循环可以优化为:

    for(int j=vol[i]; j<=bag; j++)
    {
        dp[j] = max(dp[j], dp[j-vol[i]]+val[i]);
    }
    

    注意到,在这里,内层循环是必须是从 1bag 正序扫描的,这是与 01背包的不同之处。

    从上述的二维形式的转移方程可以看出,(dp[i,j]​)依赖于其“上一行”的 (dp[i-1,j]​) 以及“同一行”的 (dp[i,j-vol[i]]​)。如果是逆序扫描,那么 (dp[i,j-vol[i]]​) 的值仍然是“上一行”的。

  • 相关阅读:
    jQuery命名空间,自定义空间及属性,插件开发全解析 (转)
    打印输入表单中的内容
    js 网页烟花效果
    js学习cookie封装之获取
    js学习cookie封装之添加
    git基本使用
    最简单的手风琴效果
    js学习cookie封装之删除
    21个值得收藏的javas技巧
    javascript常用特效汇总
  • 原文地址:https://www.cnblogs.com/sinkinben/p/11673555.html
Copyright © 2011-2022 走看看