01背包问题(DP求解)

2021-12-21  本文已影响0人  Epimenides

题目介绍

N 件物品和一个容量为 V 的背包,每件物品有各自的价值且只能被选择一次,要求在有限的背包容量下,装入的物品总价值最大。

0-1背包问题

0-1背包问题是较为简单的动态规划问题,也是其他背包问题的基础。

动态规划是不断决策求最优解的过程,0-1背包问题即是不断对第i个物品做出决策,0-1就是代表选择或者不选择两种决定。

DP问题思路分解
相关概念
  1. f[i][j]:代表在前i件物品中选,总体积小于j的集合的最大值。

    • 背包问题可以被视为选择问题,选择问题的状态表示都是相似的,第一维是选择前i个物体,后面一维表示限制。

    • 当前的状态依赖于之前的状态,从f[0][0] = 0开始决策,有N件商品,则需要进行N次0-1决策。状态f[i][j]就是由之前的状态不断更新而来。

  2. 遇到(j < v[i])背包容量不够的情况时,前i件物品最优解即为i-1件物品的最优解

    f[i][j] = f[i - 1][j];
    
  3. 当背包容量够时,可以选,那么就要从选择与不选择第i件物品的最优解中取最优解。

    f[i][j] = f[i - 1][j - v[i]] + w[i]; // 选择第i件物品
    f[i][j] = f[i - 1][j]                     // 不选择第i件物品
    f[i][j] = max(f[i - 1][j - v[i]] + w[i], f[i - 1][j]);
    
二维数组解题
#include <iostream>

using namespace std;

const int N = 1e3 + 10;

int n, m;
int f[N][N];
int v[N], w[N];

int main()
{
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++) cin >> v[i] >> w[i];
    
    for (int i = 1; i <= n; i ++)
        for(int j = 0; j <= m; j ++)
        {
            f[i][j] = f[i - 1][j];
            if (v[i] <= j) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
        }
        
    cout << f[n][m] << endl;
    
    return 0;
}
一维数组题解
  1. 为什么可以优化到一维数组版本呢?

    • f[i][j]可以求得任意合法的ij最优解,但是题目只要求得最终状态f[n][m],因此我们可以用一维数组更新
    • f[j]: N件物品,背包容量为j的最优解
  2. 为什么枚举背包容积j的时候从m开始逆序呢?

      1. 变量i的第n层循环时,f[N][j - v[i]]f[N][j]先算出来, 所以f[N][j-v[i]]是在第n层算出来的。

      2. 如果是逆序,f[n][j]要比f[n][j - v[i]]先算出来,所以就是用f[n - 1][j - v[i]]来更新

        f[j] = max(f[j], f[j - v[i]] + w[i]);
        //  如果是顺序怎等价为 f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i]) 
        
#include <iostream>

using namespace std;

const int N = 1e3 + 10;

int n, m;
int f[N];
int v[N], w[N];


int main()
{
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++) cin >> v[i] >> w[i];
    
    for (int i = 1; i <= n; i ++)
        for (int j = m; j >= v[i]; j --)
            f[j] = max(f[j], f[j - v[i]] + w[i]);
            
    cout << f[m] << endl;
    return 0;
}
上一篇 下一篇

猜你喜欢

热点阅读