ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

01背包问题四种可能解法

2022-07-01 19:32:05  阅读:260  来源: 互联网

标签:01 int max 背包 体积 数组 include 解法


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,int,max,背包,体积,数组,include,解法
来源: https://www.cnblogs.com/lucky-light/p/16435758.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有