完全背包问题的几种求解方法

C++

完全背包问题

/*
 * 完全背包问题
 * 问题描述:
 *      有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
 *      第 i 种物品的体积是 vi,价值是 wi。
 *      求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
 *      输出最大价值。
 * 
 * 数据范围:  
 *      0 < N, V ≤ 1000
 *      0 < vi, wi ≤ 1000
 * 背包问题都是算法的经典题目,下面给出 完全背包 问题的解题思路:
 *
 * 解题思路1:
 *    数组定义:
 *      f[i][j] 表示前 i 个物品,体积为 j 的最大价值
 *    递归方程:
 *      f[i][j] = max(f[i-1][j], f[i-1][j-vi]+wi, f[i-1][j-2vi]+2wi, f[i-1][j-3vi]+3wi, ..., f[i-1][j-kvi]+kwi k=j//vi)
 *                max(f[i-1][j], f[i][j-vi]+wi)
 *    初始化结果:
 *      memset(f, 0xcf, sizeof f);
 *      f[0][0] = 0
 *    最终结果
 *      max(f[n][0], f[n][1], ..., f[n][m])
 *    复杂度分析:
 *      时间复杂度为 O(NM)
 *    tips:
 *      可以使用滚动数组,或者是变换内部循环顺序,将空间复杂度优化到 O(M)
 *      for (int i = 1; i <= n; i ++ ) {
 *          for (int j = v[i]; j <= m; j ++ ) {
 *              f[j] = max(f[j], f[j - v[i]] + w[i]);
 *          }
 *      }
 *
 * 解题思路2:
 *    数组定义:
 *      f[i][j] 表示前 i 个物品,体积小于等于为 j 的最大价值
 *    递归方程:
 *      f[i][j] = max(f[i-1][j], f[i-1][j-vi]+wi, f[i-1][j-2vi]+2wi, f[i-1][j-3vi]+3wi, ..., f[i-1][j-kvi]+kwi k=j//vi)
 *                max(f[i-1][j], f[i][j-vi]+wi)
 *    初始化结果:
 *      memset(f, 0, sizeof f);
 *    最终结果
 *      f[n][m]
 *    复杂度分析:
 *      时间复杂度为 O(NM)
 *    tips:
 *      可以使用滚动数组,或者是变换内部循环顺序,将空间复杂度优化到 O(M)
 *      for (int i = 1; i <= n; i ++ ) {
 *          for (int j = v[i]; j <= m; j ++ ) {
 *              f[j] = max(f[j], f[j - v[i]] + w[i]);
 *          }
 *      }
 *
 * 解题思路3:
 *    数组定义:
 *      f[i][j] 表示前 i 个物品,价值等于 j 的最小体积
 *    递归方程:
 *      f[i][j] = min(f[i-1][j], f[i-1][j-wi]+vi, f[i-1][j-2wi]+2vi, f[i-1][j-3wi]+3vi, ..., f[i-1][j-kwi]+kvi k=j//vi)
 *              = min(f[i-1][j], f[i][j-wi]+vi)
 *    初始化结果:
 *      memset(f, 0x3f, sizeof f);      INF
 *      f[0][0] = 0
 *    最终结果
 *      便利f[n][0-W] W 为最大的可能价值,找到满足 体积 m 的价值。
 *    复杂度分析:
 *      时间复杂度为 O(NW),本题而言 W 过高
 *    tips:
 *      可以使用滚动数组,或者是变换内部循环顺序,将空间复杂度优化到 O(M)
 *
 * 解题思路4:
 *    数组定义:
 *      f[i][j] 表示前 i 个物品,价值大于等于 j 的最小体积   
 *      这个需要注意状态为 负数的时候,是合法的。
 */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

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


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][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;
}

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][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 = solution_one();

    printf("%d\n", res);
    return 0;
}




posted @ 2022-07-02 11:48  lucky_light  阅读(312)  评论(0编辑  收藏  举报