1. 问题描述
N个物品,总体积V的容量,每个物品有两种选择0:不选择该物品; 1:选择该物品
2. 题目描述举例
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
样例
输入样例:
4 5
1 2
2 4
3 4
4 5
输出样例:
8
3. 分析
3.1 暴力求解
直接思考,对于每个物品而言,我们的选择只有两种:
(1)不选择该物品放入背包
(2)选择该物品放入背包
暴力穷举所有的情况,对于每个物品都有选择和不选择两种情况,可以用1位二进制0-1表示选择或者不选择两种情况,时间复杂度O(2^n)
3.2 DP思想(n * m)求解
问题关键点:
(1)对于第i个物品选择,对于后面的物品没有任何影响,即无后效性(复习时思考这一点)
(2)对于每个物品,只有两种选择情况,选择和不选择
对于DP问题,按照yxc的分析思路,我们思考几个问题
1. 状态表示 F[i, j]
(1)集合表示
:对于物品的选择构成的选法集合,满足两个条件:(1)所有从前 i
个物品当中选择;(2)选择的总体积小于等于 j
(2)集合属性
:一般都是最大值、最小值、或者数量等等。在0-1背包当中属性是最大值,
准确来说是所有从前i个物品当中挑选,总体积小于等于 j
的所有可能选法的价值最大值
2. 状态计算
状态计算
对应着 集合划分
,对于0-1背包问题,可以按照第 i
个物品选择和不选择分成两个子集合
(1)对于不选择第 i
个物品的子集:
问题就转换成从 [1,2,...i]
个物品当中挑选,总体积小于等于 j
的所有可能方案,并且不选择第 i
个物品的可能方案,即等价于从 [1,2...i-1]
个物品当中挑选,总体积小于等于 j
的子集 F[i-1, j]
;
(2) 对于选择第 i
个物品的子集:
问题就转换成从 [1,2....i]
个物品当中挑选,总体积小于等于 j
的所有可能方案,并且选择第 i
个物品的可能方案,即等价于:由于已经选择了第i个物品,那么剩余的 [1,2,...i-1]
个物品能够挑选的最大体积变成 j - v[i]
, 也即等价于子集 F[i-1][j - v[i]] + w[i]
综上:状态计算表达式等于 F[i][j] = max(F[i-1][j], F[i - 1][j - v[i]] + w[i])
C 代码
#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#define LEN 1010
int N, V;
int v[LEN];
int w[LEN];
int F[LEN];
int main()
{
scanf("%d%d", &N, &V);
for(int i = 1; i <= N; i++){
scanf("%d%d", &v[i], &w[i]);
}
//init F[0-N][0-V]
for(int i = 1; i <= N; i++){
for(int j = V; j >= v[i]; j--){
F[j] = fmax(F[j], F[j - v[i]] + w[i]);
}
}
printf("%d\n", F[V]);
return 0;
}