背包问题
(1)01背包问题
二维
import java.util.*;
public class Main{
public static void main(String[] args){
int N = 1010;
int[][] f = new int[N][N];//表示最大价值
int[] w = new int[N];//每件物品的价值
int[] v = new int[N];//每件物品的体积
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();//物品总的数量
int m = sc.nextInt();//背包的容积
for(int i = 1; i <= n; i ++){
v[i] = sc.nextInt();
w[i] = sc.nextInt();
}
for(int i = 1; i <= n; i ++){
for(int j = 0; j <= m; j ++){
f[i][j] = f[i - 1][j];//集合划分的左边
if(v[i] <= j) f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i]] + w[i]);//当第i件物品的体积小于j时,才有左边右边取最大值
}
}
System.out.print(f[n][m]);
}
}
一维优化
import java.util.*;
public class Main{
public static void main(String[] args){
int N = 1010;
int[] f = new int[N];//表示最大价值
int[] w = new int[N];//每件物品的价值
int[] v = new int[N];//每件物品的体积
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();//物品总的数量
int m = sc.nextInt();//背包的容积
for(int i = 1; i <= n; i ++){
v[i] = sc.nextInt();
w[i] = sc.nextInt();
}
for(int i = 1; i <= n; i ++){
for(int j = m; j >= v[i]; j --){//这里原来j是从0开始的,但是由于当v[i] <= j就没有意义了,所以直接从v[i]开始
f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
}
}
System.out.print(f[m]);
}
}
(2)完全背包问题
二维(三重循环)
import java.util.*;
import java.io.*;
public class Main{
public static void main(String[] args)throws IOException{
int N = 1010;
int[] v = new int[N];
int[] w = new int[N];
int[][] f = new int[N][N];
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] str = br.readLine().split(" ");
int n = Integer.parseInt(str[0]);
int m = Integer.parseInt(str[1]);
for(int i = 1; i <= n; i ++){
String[] arr = br.readLine().split(" ");
v[i] = Integer.parseInt(arr[0]);
w[i] = Integer.parseInt(arr[1]);
}
for(int i = 1; i <= n; i ++){
for(int j = 0; j <= m; j ++){
for(int k = 0; k * v[i] <= j; k ++){
//和01背包问题的主要区别在这里
f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k);
}
}
}
System.out.print(f[n][m]);
}
}
二维(两重循环)
import java.util.*;
import java.io.*;
public class Main{
public static void main(String[] args)throws IOException{
int N = 1010;
int[] v = new int[N];
int[] w = new int[N];
int[][] f = new int[N][N];
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] str = br.readLine().split(" ");
int n = Integer.parseInt(str[0]);
int m = Integer.parseInt(str[1]);
for(int i = 1; i <= n; i ++){
String[] arr = br.readLine().split(" ");
v[i] = Integer.parseInt(arr[0]);
w[i] = Integer.parseInt(arr[1]);
}
for(int i = 1; i <= n; i ++){
for(int j = 0; j <= m; j ++){
f[i][j] = f[i - 1][j];
//和01背包问题的主要区别在这里
if(v[i] <= j) f[i][j] = Math.max(f[i][j], f[i][j - v[i]] + w[i]);
}
}
System.out.print(f[n][m]);
}
}
一维优化
import java.util.*;
import java.io.*;
public class Main{
public static void main(String[] args)throws IOException{
int N = 1010;
int[] v = new int[N];
int[] w = new int[N];
int[] f = new int[N];
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] str = br.readLine().split(" ");
int n = Integer.parseInt(str[0]);
int m = Integer.parseInt(str[1]);
for(int i = 1; i <= n; i ++){
String[] arr = br.readLine().split(" ");
v[i] = Integer.parseInt(arr[0]);
w[i] = Integer.parseInt(arr[1]);
}
for(int i = 1; i <= n; i ++){
for(int j = v[i]; j <= m; j ++){
//这里的j不需要逆序输入
f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
}
}
System.out.print(f[m]);
}
}
(3)多重背包问题
4. 多重背包问题 I - AcWing题库 二维(三重循环)
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int N = 110;
int[] v = new int[N];
int[] w = new int[N];
int[] s = new int[N];
int[][] f = new int[N][N];
for(int i = 1; i <= n; i ++){
v[i] = sc.nextInt();
w[i] = sc.nextInt();
s[i] = sc.nextInt();
}
for(int i = 1; i <= n; i ++){
for(int j = 0; j <= m ; j ++){
for(int k = 0; k * v[i] <= j && k <= s[i]; k ++){//与完全背包问题的主要不同
f[i][j] = Math.max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
}
}
}
System.out.print(f[n][m]);
}
}
5. 多重背包问题 II - AcWing题库 (给的数据范围不一样)
二进制优化
第一种写法
import java.util.*;
public class Main{
public static void main(String[] ags){
Scanner scan = new Scanner(System.in);
int N = 12000;
//一个数最多就是2的12次方就会超过题目给的2000,所以1000*12
int[] v = new int[N];
int[] w = new int[N];
int[] f = new int[N];
int n = scan.nextInt();
int m = scan.nextInt();
//这个cnt是组合的个数
int cnt = 0;
for(int i = 0 ; i < n ; i ++ ){
int a = scan.nextInt();//体积
int b = scan.nextInt();//价值
int s = scan.nextInt();//数量
int k = 1; //2的0次幂
//将每一个s都分解成2的多少次幂合
while(s >= k){
cnt ++ ;
v[cnt] = a * k; 这个组合的体积总数
w[cnt] = b * k; //这个组合的价值总量
s -= k; // s 分解成2的0到k次幂,所以每次s都要减去当前的k
k *= 2; // k每次都乘以2
}
//判断k的很多次幂加起来是不是还差一点才到s
//假设s为10,那么就是1,2,4,3
if(s > 0){
cnt ++;
v[cnt] = a * s;//这里的s就是上面例子中的3
w[cnt] = b * s;
}
}
n = cnt; // 将所有组合的值赋值给n
//用01背包问题的模板来做
for(int i = 1 ; i <= n ; i ++ )
for(int j = m ; j >= v[i] ; j --)
f[j] = Math.max(f[j],f[j - v[i]] + w[i]);
System.out.println(f[m]);
}
}
第二种写法
import java.util.*;
//多重背包问题的二进制的第二种写法
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int N = 2010;
int[] f = new int[N];
int n = sc.nextInt();
int m = sc.nextInt();
for(int i = 1; i <= n; i ++){
int v = sc.nextInt();
int w = sc.nextInt();
int s = sc.nextInt();
for(int k = 1; k <= s; k *= 2){
for(int j = m; j >= k * v; j --){
f[j] = Math.max(f[j], f[j - v * k] + w * k);
}
s -= k;
}
if(s > 0){
for(int j = m; j >= s * v; j --){
f[j] = Math.max(f[j], f[j - v * s] + w * s);
}
}
}
System.out.print(f[m]);
}
}
6. 多重背包问题 III - AcWing题库
单调队列优化
AcWing 6. 多重背包问题 III (包含自己的一些理解,不懂的可以看看) - AcWing
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int N = 1010;
int M = 20010;
int[][] f = new int[N][M];
int[] q = new int[M];//单调队列
int[] v = new int[M];//体积
int[] w = new int[M];//价值
int[] s = new int[M];//数量
int n = sc.nextInt();//物品个数
int m = sc.nextInt();//背包数量
for(int i = 1; i <= n; i ++){
v[i] = sc.nextInt();
w[i] = sc.nextInt();
s[i] = sc.nextInt();
}
for(int i = 1; i <= n; i ++){
for(int r = 0; r < v[i]; r ++){//表示余数
int hh = 0;//队头
int tt = -1;//队尾
for(int j = r; j <= m; j += v[i]){
f[i][j] = f[i - 1][j];//一开始不放物品
while(hh <= tt && q[hh] < j - v[i] * s[i]) ++ hh;//队头超出滑动窗口
//让最大的值移到队头
while(hh <= tt && f[i - 1][q[tt]] + (j - q[tt]) / v[i] * w[i] <= f[i - 1][j]) tt --;
q[++ tt] = j;
f[i][j] = f[i - 1][q[hh]] + (j - q[hh]) / v[i] * w[i];
}
}
}
System.out.print(f[n][m]);
}
}
(4)分组背包问题
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int N = 110;
int[][] v = new int[N][N];
int[][] w = new int[N][N];
int[] f = new int[N];
int[] s = new int[N];
int n = sc.nextInt();
int m = sc.nextInt();
for(int i = 1; i <= n; i ++){
//分成n组,每一组的数量
s[i] = sc.nextInt();
//每一组中的物品的体积和价值
for(int j = 1; j <= s[i]; j ++){
v[i][j] = sc.nextInt();
w[i][j] = sc.nextInt();
}
}
//和01背包问题很像
for(int i = 1; i <= n; i ++){
for(int j = m; j >= 0; j --){
for(int k = 0; k <= s[i]; k ++){
if(v[i][k] <= j) f[j] = Math.max(f[j], f[j - v[i][k]] + w[i][k]);
}
}
}
System.out.print(f[m]);
}
}