背包问题
01背包问题
二维求解
/**
* 01 背包问题
* 物品只能选择一次
*
* 二维dp dp[n][m] 使用m价值 购买n件的最高价值
*
* 1、dp[i-1][j] 第i件不购买
* 2、dp[i-1][j - v[i]] + w[i] 购买第i件
* 状态转移方程 dp[i][j] = max(dp[i-1][j], dp[i-1][j - v[i]] + w[i])
*
* 初始化 dp[n][m] 初始化为0 表示未进行购买
*/
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] input = in.nextLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
int[][] dp = new int[n + 1][m + 1];
int[] v = new int[n + 1];
int[] w = new int[n + 1];
for(int i = 1; i <= n; i++){
input = in.nextLine().split(" ");
v[i] = Integer.parseInt(input[0]);
w[i] = Integer.parseInt(input[1]);
}
for(int i = 1; i <= n; i++){
for(int j = 0; j <= m; j ++){
dp[i][j] = dp[i - 1][j];
if(j >= w[i]){
dp[i][j] = Math.max(dp[i][j], dp[i-1][j - v[i]] + w[i]);
}
}
}
System.out.println(dp[n][m]);
}
}
一维求解
import java.util.Scanner;
public class Main {
/**
* 01背包降维
*
* dp[m] 表示 使用体积,能够购买到的最大价值
*
* 1、 dp[j] 表示不进行购买第i件商品
* 2、dp[j - v[i]] + w[i] 表示购买第i件商品
*
* 状态转移方程
* dp[j] = max(dp[j], dp[j - v[i]] + w[i])
*
* 初始化 为0
*/
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] input = in.nextLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
int[] dp = new int[m + 1];
int[] v = new int[n + 1];
int[] w = new int[n + 1];
for (int i = 1; i <= n; i++) {
input = in.nextLine().split(" ");
v[i] = Integer.parseInt(input[0]);
w[i] = Integer.parseInt(input[1]);
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}
System.out.println(dp[m]);
}
}
完全背包问题
一维求解
import java.util.Scanner;
public class Main {
/**
* 完全背包问题
*
* 状态转移方程
* dp[j] = max(dp[j], dp[j - v[i]] + w[i])
*
*/
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] input = in.nextLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
// 表示使用体积 能够购买到的最大价值
int[] dp = new int[m + 1];
for (int i = 1; i <= n; i++) {
input = in.nextLine().split(" ");
int v = Integer.parseInt(input[0]);
int w = Integer.parseInt(input[1]);
// 循环遍历 多次购买
for(int j = v; j <= m; j++){
dp[j] = Math.max(dp[j], dp[j - v] + w);
}
}
// 最优解为dp数组最后一项
System.out.println(dp[m]);
}
}
多重背包问题
1、遍历求解
import java.util.Scanner;
public class Main {
/**
* 多重背包问题
* 将购买次数平铺等价于01背包问题
*
* 状态转移方程
* dp[j] = max(dp[j], dp[j - v[i]] + w[i])
*
*/
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] input = in.nextLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
// 表示使用体积 能够购买到的最大价值
int[] dp = new int[m + 1];
for (int i = 1; i <= n; i++) {
input = in.nextLine().split(" ");
int v = Integer.parseInt(input[0]);
int w = Integer.parseInt(input[1]);
int s = Integer.parseInt(input[2]);
// 类比于01背包问题遍历数组由m开始
for(int j = m; j >= v; j--){
// 将购买次数平铺为 体积为 k * v 价值为 k * w的购买次数
for(int k = 1; k <= s && k * v <= j; k++){
dp[j] = Math.max(dp[j], dp[j - k * v] + k * w);
}
}
}
// 最优解于dp数组最后一项
System.out.println(dp[m]);
}
}
2、二进制优化
import java.util.Scanner;
public class Main {
/**
* 多重背包问题
* 将购买次数平铺等价于01背包问题
*
* 采用二进制展开次数s, 二进制展开能表示s以下任何数
*
* 状态转移方程
* dp[j] = max(dp[j], dp[j - v[i]] + w[i])
*
*/
private static int LENGTH = 100010;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] input = in.nextLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
// 表示使用体积 能够购买到的最大价值
int[] dp = new int[LENGTH];
int[] V = new int[LENGTH];
int[] W = new int[LENGTH];
int index = 1;
for (int i = 1; i <= n; i++) {
input = in.nextLine().split(" ");
int v = Integer.parseInt(input[0]);
int w = Integer.parseInt(input[1]);
int s = Integer.parseInt(input[2]);
// 二进制展开
int j = 1;
while(j <= s){
V[index] = v * j;
W[index] = w * j;
s -= j;
j = j * 2;
index++;
}
if (s > 0) {
V[index] = v * s;
W[index] = w * s;
index++;
}
}
for (int i = 1; i < index; i++) {
for (int j = m; j >= V[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - V[i]] + W[i]);
}
}
System.out.println(dp[m]);
}
}