题目描述:
有 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
分析步骤:
第一:对于背包问题我们可以分为状态表示,和状态计算。我们如何去表示我们要求出的集合答案是我们背包问题最关键的问题,我们所有的表达式都要围绕这一条语句去构建。
-
状态表示:我们定义集合为:对于所有前 i 个物体且总体积不超过 j 的选法的集合!!!取其中的最大值!!
-
状态计算:由于所有的物品我们只有选与不选的问题所以我们将其分为两种类型来计算一种是:所有不选第i个物品的方案;第二种是:所有选择第 i 个物品的集合。只要把这两种状态去计算就可以完全包括所有的方案因为只有选与不选,选了的话就在第一种情况,不选就在第二种情况,不会存在薛定谔的选择:既选了又没选这是不可能的。
-
状态计算的第一种情况(不选第i种的方案)我们仔细想想如果你不选这个物品的话,其实你现在的状态和dp[i-1][j]的情况是一样的,因为前面的方案也是没选第i种,而背包体积是不变的所以我们表达式就应该是 ans[i][j] = ans[i-1][j];
-
状态计算的第二种情况(选择第i种方案)我们仔细想向如果我们选择这种物品的话前面(1~i-1)的情况是已经确定好了的是确定不变了的,唯一有所改变的就是你现在要选的这个第i个物品。所以总结一下:那部分不变的是你在(1,i-1)的物品范围内,背包体积在(0,j-vi)的情况下所能得到的最大值,也就是dp(i-1,j-v[i]);那部分变化的就是你要选择的所以应该在不变的情况下加上w[i]就可以。然后去一下两种情况的最大值就可以了。
-
注意一下,我们状态划分是分为两种状态,但是可能第二种情况不会存在,因为有可能背包装不下第i种物品。
-
01背包从后往前,完全背包从前往后!!!
第二:书写主函数,构建整体框架:
-
书写背包问题的代码时候,我们一定要记得我们的集合定义,一切的一切都是围绕着一句话来展开的。
-
用两个for i 代表物品,j代表背包体积。j可以从0开始,因为根据我们的定义:对于所有前 i 个物体且总体积不超过 j 的选法的集合!!!取其中的最大值!!总体积可以为0,因为我们可以不选择物品放入我们的背包。所体积可以是0。
-
如果是不选择物品的话那么情况就和之前(1,i-1)的情况一致
-
如果是选择物品还要判断一下我们背包是否可以放入物品,体积大过了物品的体积就可以放入则我们的答案就应该在前一部分不动的里面取出最大值再加上物品i的价值,和不选的方案取最大值。
for(int i = 1 ; i <= n ; i ++){for(int j = 0 ; j <= m ; j ++){ans[i][j] = ans[i-1][j];if(j >= v[i]){ans[i][j] = max(ans[i][j] , ans[i-1][j-v[i]] + w[i]);}}}
代码:
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 1010;int n , m ;
int v[N] , w[N];
int ans[N][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 ++){ans[i][j] = ans[i-1][j];if(j >= v[i]){ans[i][j] = max(ans[i][j] , ans[i-1][j-v[i]] + w[i]);}}}cout<<ans[n][m];return 0 ;
}