01背包问题四种可能解法

2022/7/1 23:23:45

本文主要是介绍01背包问题四种可能解法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

c++

01 背包问题

/*
 * 0, 1 背包问题
 *
 * 问题描述:
 *      有 n 件物品和一个容量是 m 的背包。每件物品只能使用一次。
 *      第 i 件物品的体积是 vi,价值是 wi。
 *      求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
 *      输出最大价值。
 *
 *
 * 动态规划求解:
 *      动态规划掰开了说,就是使用记忆数组记录之前计算的数据结果,然后中间使用记忆数组来求解。
 *      一下给出几种记忆数组策略,并给出他们的优劣性。
 *      算法思路1:
 *        数组定义:
 *          f[i][j] 表示仅选择前 i 个物品,体积为 j 的最大价值。
 *        递归方程:
 *          f[i][j] = max(f[i-1][j], f[i-1][j-v[j]] + w[j]) // 如果 j - v[j] >= 0的话
 *        初始化方法:
 *          memset(f, 0xcf, sizeof f)   注意这个是最小值 0xcf,因为我们求得是 max
 *          f[0][0] = 0
 *        最终结果:
 *          max(f[n][0], ..., f[n][m])
 *        复杂度:
 *          O(NM)
 *        tips:
 *          空间复杂度可以优化到 O(M),使用滚动数组,或者是使用考虑这个数组的前后性
 *          for (int i = 1; i <= n; i ++ ) {
 *              for (int j = m; j >= v[i]; j ++ ) {
 *                  f[j] = min(f[j], f[j - v[i]] + w[i])    // 这里的变量方式保证了 f[j - v[i]] 是来自于 i - 1 的
 *              }
 *          }
 *
 *      算法思路2:
 *        数组定义:
 *          f[i][j] 表示仅选择前 i 个物品,体积小于等于 j 的最大价值。
 *        递归方程:
 *          f[i][j] = max(f[i-1][j], f[i-1][j-v[j]] + w[j]) // 如果 j - v[j] >= 0的话
 *        初始化方法:
 *          memset(f, 0, sizeof f)
 *        最终结果:
 *          f[n][m]
 *        复杂度:
 *          O(NM)
 *        tips:
 *          同样是可以按照解决方案 1 进行空间优化。
 *
 *      算法思路3:
 *        数组定义:
 *          f[i][j] 表示仅选择前 i 个物品,价值等于 j 的最小体积。
 *        递归方程:
 *          f[i][j] = min(f[i-1][j], f[i-1][j-w[j]] + v[j]) // 如果 j - w[j] >= 0的话
 *        初始化方法:
 *          memset(f, 0x3f, sizeof f)
 *          f[0][0] = 0
 *        最终结果:
 *          遍历所有的价值,找到 f[n][i] 中体积满足的最大 i
 *        复杂度:
 *          O(NV), V 为 VALUE 的限度,因为本题最大 V = 1000 * 1000 因此,复杂度过高
 *        tips:
 *          同样是可以按照解决方案 1 进行空间优化。
 *
 *      算法思路4:
 *        数组定义:
 *          f[i][j] 表示仅选择前 i 个物品,价值大于等于 j 的最小体积。
 *        递归方程:
 *          f[i][j] = min(f[i-1][j], f[i-1][j-w[j]] + v[j]) // 如果 j - w[j] >= 0的话
 *          f[i][j] = min(f[i-1][j], 0 + v[j]) // 如果 j - w[j] >= 0的话,相当于 f[i][j <= 0] = 0
 *        初始化方法:
 *          memset(f, 0x3f, sizeof f)
 *          f[0][0] = 0
 *          初始化乍一看和 算法思路4一样,但是其实不同,因为 第二维度 为负数是成立的
 *        最终结果:
 *          遍历所有的价值,找到 f[n][i] 中体积满足的最大 i。
 *          但是借助其单调性,可以二分。
 *        复杂度:
 *          O(NV), V 为 VALUE 的限度,因为本题最大 V = 1000 * 1000 因此,复杂度过高
 *        tips:
 *          同样是可以按照解决方案 1 进行空间优化。
 */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

const int N = 1010, M = N;
const int INF = 0x3f3f3f3f, _INF = 0xcfcfcfcf;
int n, m;
int w[N], v[N]; // v for volumn, w for weight
int f[N][M];

// 解决方案 1
int solution_one() {
    memset(f, 0xcf, sizeof f);
    f[0][0] = 0;
    for (int i = 1; i <= n; i ++ ) {
        for (int j = 0; j <= m; j ++ ) {
            if (j >= v[i]) {
                f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
            } else {
                f[i][j] = f[i - 1][j];
            }
        }
    }

    int res = _INF;
    for (int i = 0; i <= m; i ++ ) {
        res = max(res, f[n][i]);
    }
    return res;
}


// 解决方案 2
int solution_two() {
    memset(f, 0, sizeof f);
    for (int i = 1; i <= n; i ++ ) {
        for (int j = 0; j <= m; j ++ ) {
            if (j >= v[i]) {
                f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
            } else {
                f[i][j] = f[i - 1][j];
            }
        }
    }

    int res = f[n][m];
    return res;
}


int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) {
        scanf("%d%d", &v[i], &w[i]);
    }

    int res = _INF;
    res = solution_two();
    printf("%d\n", res);

    return 0;
}




这篇关于01背包问题四种可能解法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程