背包问题
下列实现均为朴素解法,普通但是容易理解!先学会走再学跳嘛!
0-1背包
描述
有N种物品和一个容量为V的背包,每种物品只能使用一次。
第 i 种物品的体积为vi,价值为wi。
问如何放置这些物品,在不超过背包容量的前提下,使得背包内物品价值达到最大值?
样例
输入:
4 5
1 2
2 4
3 4
4 5
输出:
8
思路
解析方法
分析状态,推导状态转移方程
1.分析状态时,先分析问题的集合和属性
- 集合:满足“条件”的所以集合
- 属性:最值/数量…
- 状态=属性+集合
2.推导状态转移方程,即如何划分上述的集合
解析过程
状态:f[i][j]
f[i][j] 表示 :所有满足 在前 i-1 种物品中选择,且总体积不超过 j 的方案的最大值。
状态转移方程:
在0-1背包问题中,每种物品只能使用一次,所以集合划分就是选还是不选的问题。
综上所述,状态转移方程为:
当前物品i 的体积 > 背包剩余容量==不选择第 i 种物品
j< v[i] :
f[i][j]=f[i-1][j]
当物品i的体积<背包剩余容量:
需要在选择 i 和不选 i 物品集合中,找出可以使价值达到最大的方案。
(也就是说:在众多选择方案中,基于之前的选择,我在选了a 和不选 a 的两种情况下,哪一种可以我背包中的物品价值更大)
j>=v[i] :
f[i][j]=max{f[i-1][j],f[i-1][j-v]+w}
实现
import java.util.Scanner;
public class Main{
/**
* 问题:问怎样放置物品在不超过背包容量的情况下,达到商品价值的最大值
* 背包问题:每个物品只能使用一次
* 状态 dp[i][j]:表示 所有前i个物品,总体积<= j的方案的最大值
* 状态转移:
* 不选择第i种物品:dp[i][j]=dp[i-1][j]
* (不选择第i种物品时,当前状态下的最大值仍然是前i-1种商品在总体积不超过j的所有方案中的最大值)
* 选择第i种物品:dp[i][j]=dp[i-1][j-v[i]]+w[i]
* (选择第i种物品的方案中最大值=前i-1种物品,放在容量为j-v[i]的背包中的最大值+第i件物品的价值)
* 状态转移方程
* v[i]<=j时,dp[i][j]=max{dp[i-1][j],dp[i-1][j-v[i]]+w[i]}
* v[i]>j时, dp[i][j]=dp[i-1][j] (当前物品的添加超出总体积约束范围,不选该物品)
* 状态初始化
* dp[0][0]=0:当在背包不放任何物品时(体积总和为0),背包物品价值(最大值)为0
*/
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int N=sc.nextInt();
int V=sc.nextInt();
//一个长度为N的数组,第i个元素表示第i个物品的体积、价值
int[] v=new int[N+1];
int[] w=new int[N+1];
for (int i = 1; i <= N; i++) {
v[i]=sc.nextInt();
w[i]=sc.nextInt();
}
// N+1,V+1 表示:
// 第0行表示只能选择第0个物品的时候,即没有物品的时候
// 第0列表示背包的体积为0的时候,即不能装任何东西的时候
int[][] dp=new int[N+1][V+1];
//状态初始化
dp[0][0]=0;
//枚举各种情况,找出符合条件的最大值
for (int i = 1; i <= N; i++) {
for (int j = 0; j <=V; j++) {
if (j>=v[i]){
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
}else {
dp[i][j]=dp[i-1][j];
}
}
}
//最后dp[N][V]就是题目中要求的答案!
System.out.println(dp[N][V]);
}
}
完全背包
有N种物品和一个容量为V的背包,每种物品都可以无限次使用。
第i种物品的体积为vi,价值为wi。
问如何放置这些物品,在不超过背包容量的前提下,使得背包内物品价值达到最大值?
样例
输入:
4 5
1 2
2 4
3 4
4 5
输出:
10
思路
按照0-1背包的分析方法,我们再来看完全背包问题
完全背包和0-1背包在题目描述上的唯一区别:物品可以无限次使用
从而可以判断,在划分集合时,会有所不同。
状态:f[i][j]
所有满足 在前 i-1 种物品中选择,且总体积不超过 j 的方案的最大值。
状态转移方程:
实现
public class Main{
/**
* 过程分析:
* 状态
* 集合:所有满足在前i的物品中,物品总价值<=背包容量j的方案的集合
* 属性:所有满足条件集合的最大值
* 状态方程
* 当第i个物品使用0次(就是不往包里装i物品),dp[i][j]=dp[i-1][j]
* 当第i个物品使用K次,dp[i][j]=dp[i-1][j-k*v[i]]+k*w[i]
*
* dp[i][j]=max{dp[i-1][j],dp[i-1][j-v]+w,dp[i-1][j-2v]+2w....}
* dp[i][j-v]=max{dp[i-1][j-v],dp[i-1][j-2v]+w,dp[i-1][j-3v]+2w....}
* dp[i][j]=max{dp[i-1][j],dp[i][j-v]+w}
*/
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int N=sc.nextInt();
int V=sc.nextInt();
int[] v=new int[N+1];
int[] w=new int[N+1];
for(int i=1;i<=N;i++){
v[i]=sc.nextInt();
w[i]=sc.nextInt();
}
int[][] dp=new int[N+1][V+1];
for(int i=1;i<=N;i++){
for(int j=0;j<=V;j++){
dp[i][j]=dp[i-1][j];
if(j>=v[i]){
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
}
}
}
System.out.println(dp[N][V]);
}
}
推荐文章:背包九讲