背包问题(01, 完全)

28 阅读3分钟

01背包问题O(nm)O(nm)

问题描述:有NN个物品与一个体积为VV的背包,每件物品只能使用一次了,第ii件物品的体积为v[i]v[i], 价值为w[i]w[i],求将哪些物品放入背包可使总体及不超过背包容量且价值最大。

思路:用一个二维矩阵f存储最优解,f[i][j]代表在对于前i个物品,背容量为j时,最佳的选择方案所获得的收益。若j >= v[i],则当前面临的决策问题为,第i个物品取或不取。

  1. 若不取,则其对应的状态为对于前i - 1个物品,背包容量为j时的最佳收益,即f[i][j] = f[i - 1][j]
  2. 若取,则其状态为,对于前i- 1个物品,背包容量为j - v[i]时的最佳收益 + 物品i的收益,即f[i][j] = f[i - 1][j - v[i]] + w[i]
  3. 因此状态转移方程式为:f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i])
const int N = 1010;
int f[N][N], n, m; // n为物品数量,m为背包体积
int v[N], w[N]; //v存储每个物体的体积,w存储每个物体的价值

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 = 1; j <= m; j ++ ){
            if (j >= v[i]) f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
        }
    }
    
    cout << f[n][m] << endl;
}

优化:对于数组f的第i行来说,其取值主要由f数组的第i - 1行与v[i]、w[i]来进行更新,因此,可将f数组优化为一维数组,具体代码如下:

const int N = 1010;
int f[N], n, m;
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 - v[i]]来更新f[j],所以要倒着循环
            f[j] = max(f[j - v[i]] + w[i], f[j]);
        }
    }
}

完全背包问题

问题描述:有NN个物品与一个体积为VV的背包,每件物品都有无限件可用,第ii件物品的体积为v[i]v[i], 价值为w[i]w[i],求将哪些物品放入背包可使总体及不超过背包容量且价值最大。0<N,V<10000 < N, V < 1000

思路:用一个二维矩阵f存储最优解,f[i][j]代表在对于前i个物品,背容量为j时,最佳的选择方案所获得的收益。若j >= v[i],则当前面临的决策问题为,第i个物品取或不取,以及取多个的问题。

  1. 若不取,则对应状态为前i - 1个物品,背包容量为j时的最佳方案的收益,即f[i - 1][j]
  2. 若取,则需要计算取几个时收益最大,并与不取时进行比较,即:

f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i], f[i - 1][j - 2 * v[i]] + 2 * w[i], ..., f[i - 1][j - k * v[i]] + k * w[i]), j - k * v[i] >= 0

由上述公式可知:

f[i][j - v[i]] = max(f[i - 1][j - v[i]], f[i - 1][j - 2 * v[i]] + w[i], f[i - 1][j - 3 * v[i]] + 2 * w[i], ..., f[i - 1][j - (k + 1) * v[i]] + k * w[i])

由上可知:f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i])

简化公式可得:f[j] = max(f[j], f[j - v[i]] + w[i])

#include<iostream>
using namespace std;

const int N = 1010;
int f[N], v[N], w[N], n, m;

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 = v[i]; j <= m; j ++ ){
            f[j] = max(f[j], f[j - v[i]] + w[i]); //更新背包状态
        }
    }
    
    cout << f[m] << endl;
}