背包问题专题
背包问题常见以下几种
01背包
模型特点:每种物品只有一个,且要么选要么不选,有01两种状态因而以01得名。有限制范围,常见体积或者重量限制,一般用二维数组dp[i][j]表示状态,其中第一个维度表示可选范围,第二个维度表示容量等限制范围。
完全背包
模型特点:与01背包相似,唯一不同的是,完全背包的物品是由无限个。
01背包问题
题目描述
给定N个物品,每个物品有一个重量W和一个价值V.你有一个能装M重量的背包.问怎么装使得所装价值最大.每个物品只有一个.
输入
输入的第一行包含两个整数n, m,分别表示物品的个数和背包能装重量。
以后N行每行两个数Wi和Vi,表示物品的重量和价值
输出
输出1行,包含一个整数,表示最大价值。
样例输入
4 5
2 3
1 2
3 4
2 2
先以样例数据来写帮助了解
解法一递归
package 动态规划;
public class _01背包问题_递归 {
static int []w= {2,1,3,2 };//重量表
static int []v= {3,2,4,2};//价值表
static int n=4;//物品数量
static int W=5;//背包最大承重量
//当前选到第i个物品 剩余ww的承重量
static int dfs(int i, int ww) {
if(ww<=0) return 0;//装不进去了
if(i==n) {//没东西可选了
return 0;
}
int v2=dfs(i+1,ww);//不选择该物品
if(w[i]<=ww) {
int v1=v[i]+dfs(i+1,ww-w[i]);
return Math.max(v1, v2);
}else {
//装不下 不选 返回v2子树
return v2;
}
}
public static void main(String[] args) {
int ww=W;
int ans=dfs(0,ww);
System.out.println(ans);
}
}
解法二、记忆型递归
package 动态规划;
import java.util.Arrays;
public class _01背包问题_记忆型递归 {
static int []w= {2,1,3,2 };//重量表
static int []v= {3,2,4,2};//价值表
static int n=4;//物品数量
static int W=5;//背包最大承重量
//当前选到第i个物品 剩余ww的承重量
static int[][]ret;
static int dfs(int i, int ww) {
if(ww<=0) return 0;//装不进去了
if(i==n) {//没东西可选了
return 0;
}
//计算前先查询
if(ret[i][ww]>=0) {
return ret[i][ww];
}
//返回前先记忆状态
int v2=dfs(i+1,ww);//不选择该物品
int ans= 0;
if(w[i]<=ww) {
int v1=v[i]+dfs(i+1,ww-w[i]);//选择该物品
ans=Math.max(v1, v2);
}else {
ans=v2;
}
ret[i][ww]=ans;
return ret[i][ww];
}
public static void main(String[] args) {
int ww=W;
ret =new int[n][W+1];
//初始化为-1
for(int i=0;i<n;i++) {
Arrays.fill(ret[i], -1);
}
for(int i=0;i<n;i++) {
for(int j=1;j<=W;j++) {
System.out.print(ret[i][j]);
}
System.out.println();
}
int ans=dfs(0,ww);
System.out.println(ans);
}
}
解法三、dp
/*
题目描述
给定N个物品,每个物品有一个重量W和一个价值V.你有一个能装M重量的背包.问怎么装使得所装价值最大.每个物品只有一个.
输入
输入的第一行包含两个整数n, m,分别表示物品的个数和背包能装重量。
以后N行每行两个数Wi和Vi,表示物品的重量和价值
输出
输出1行,包含一个整数,表示最大价值。
样例输入
3 5
2 3
3 5
4 7
样例输出
8
4 5
2 3
1 2
3 4
2 2
得到的dp矩阵:
0 0 3 3 3 3
0 2 3 5 5 5
0 2 3 5 6 7
0 2 3 5 6 7
*
*/
package 动态规划;
import java.util.Scanner;
public class _01背包问题_动态规划 {
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
int n=reader.nextInt();//物品个数
int m=reader.nextInt();//背包承重量
int[] w=new int[n];//存放每个物品的重量
int[] v=new int[n];//存放每个物品的价值
for(int i=0;i<n;i++) {
w[i]=reader.nextInt();
v[i]=reader.nextInt();
//dp[i][j]中第一个维度表示可选范围 第二个维度表示容量
}
int [][]dp=new int[n][m+1];
//初始化dp数组第一行
for(int i=0;i<=m;i++) {//w[0]<i 中的i刚好可以表示背包容量
if(w[0]<=i) {//放得下0号物品
dp[0][i]=v[0];
}
}
for(int i=1;i<n;i++) {
for(int j=0;j<=m;j++) {
if(w[i]<=j) {
int v1=dp[i-1][j];//不要当前这个物品
int v2=dp[i-1][j-w[i]]+v[i];//要当前这个物品
dp[i][j]=Math.max(v1, v2);
}else {
dp[i][j]=dp[i-1][j];
}
}
}
System.out.println(dp[n-1][m]);
// for(int i=0;i<n;i++) {
// for(int j=0;j<=m;j++) {
// System.out.print(dp[i][j]+" ");
// }
// System.out.println();
// }
完全背包问题
/*
有 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
输出样例:
10
链接:
https://www.acwing.com/problem/content/description/3/
*/
package 动态规划;
import java.util.Scanner;
public class 完全背包问题 {
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
int n=reader.nextInt();//物品个数
int m=reader.nextInt();//背包承重量
int[] w=new int[n];//存放每个物品的重量
int[] v=new int[n];//存放每个物品的价值
for(int i=0;i<n;i++) {
w[i]=reader.nextInt();
v[i]=reader.nextInt();
}
int[][] dp=new int[n][m+1];
//初始化dp数组第一行
for(int i=0;i<m;i++) {
if(w[0]<=i) {
dp[0][i]=v[0]+dp[0][i-w[0] ];
}
}
for(int i=1;i<n;i++) {//i表示行号 同时也是物品号
for(int j=1;j<=m;j++) {//j表示列好 同时也是背包容量
if(w[i]<=j) {
int v1=v[i]+dp[i][j-w[i]];//选当前物品
int v2=dp[i-1][j];
dp[i][j]=Math.max(v1,v2);
}else {
dp[i][j]=dp[i-1][j];
}
}
}
// for(int i=0;i<n;i++) {
// for(int j=1;j<=m;j++) {
// System.out.print(dp[i][j]+" ");
// }
// System.out.println();
// }
System.out.println(dp[n-1][m]);
}
}
}
}