🍑 算法题解专栏
🍑 多重背包Ⅰ
输入
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出
10
🍑 朴素版 O(n^3)
import java.util.Scanner;
public class Main
{
static int N = 110;
static int[] s = new int[N];
static int[] w = new int[N];// 价值 weight
static int[] v = new int[N];// 体积 volum
static int[][] f = new int[N][N];
public static void main(String[] args)
{
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();
s[i] = sc.nextInt();
}
// DP(三重循环 n^3)
for (int i = 1; i <= n; i++)// 枚举物品
for (int j = 1; j <= m; j++)// 枚举体积
{
f[i][j] = f[i-1][j];
for (int k = 0; k <= s[i]; k++)// 枚举选取当前物品的数量
if(j >= k * v[i])
f[i][j] = Math.max(f[i][j], f[i - 1][j - k * v[i]] + w[i] * k);
}
System.out.println(f[n][m]);
}
}
🍑 朴素01背包版 O(n^3)
import java.util.Scanner;
public class Main
{
static int N = 10010;
static int[] v = new int[N];
static int[] w = new int[N];
static int[][] f = new int[N][110];
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int cnt = 0; // 记录物品个数
while (n-- > 0)
{
int vv = sc.nextInt();
int ww = sc.nextInt();
int s = sc.nextInt();
while (s-- > 0)// 拆成 01 背包
{
v[++cnt] = vv;
w[cnt] = ww;
}
}
for (int i = 1; i <= cnt; i++)// 枚举物品
for (int j = 1; j <= m; j++)// 枚举体积
{
f[i][j] = f[i - 1][j];
if (j >= v[i])
f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
System.out.println(f[cnt][m]);
}
}
🍑 滚动数组01背包版 O(n^3)
import java.util.Scanner;
public class Main
{
static int N = 10010;
static int[] v = new int[N];
static int[] w = new int[N];
static int[] f = new int[110];
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int cnt = 0; // 记录物品个数
while (n-- > 0)
{
int vv = sc.nextInt();
int ww = sc.nextInt();
int s = sc.nextInt();
while (s-- > 0)// 拆成 01 背包
{
v[++cnt] = vv;
w[cnt] = ww;
}
}
for (int i = 1; i <= cnt; i++)// 枚举物品
for (int j = m; j >= v[i]; j--)// 枚举体积
{
// if (j >= v[i])
// f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i]] + w[i]);
f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
}
System.out.println(f[m]);
}
}
🍑 多重背包Ⅱ
🍍 相较 Ⅰ 加大了数据范围
🍍 将数 s 分为可以组成 它 的组合,即 log(s)个数
👨🏫 前人之述备矣
🍑 二进制优化版 O(m * nlogn)
import java.util.Scanner;
public class Main
{
static int N = 2010;
static int M = N * N;
static int f[] = new int[N];
static int v[] = new int[M];
static int w[] = new int[M];
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int cnt = 0;// 记录转化成 01背包后的物品个数
for (int i = 1; i <= n; i++)
{
int vv = sc.nextInt();
int ww = sc.nextInt();
int s = sc.nextInt();
// 将 s 个物品 转化成 log(s) 份
for (int j = 1; j <= s; j *= 2)
{
s -= j;
v[++cnt] = vv * j;
w[cnt] = ww * j;
}
if (s > 0)
{
v[++cnt] = vv * s;
w[cnt] = ww * s;
}
}
// DP
for (int i = 1; i <= cnt; 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]);
}
}
🍑 多重背包Ⅲ
🍑 单调队列优化版 O(nm)
👨🏫 前人之述备矣
🍍 单调队列存的是体积
🍤 朴素版
import java.util.Scanner;
public class Main{
static int N = 1010;
static int M = 20010;
static int n, m;
static int[][] f = new int[N][M];
static int[] v = new int[N];// 价值
static int[] w = new int[N];// 题解
static int[] s = new int[N];// 数量
static int[] q = new int[M];
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
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])
{
while (hh <= tt && j - q[hh] > s[i] * v[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.println(f[n][m]);
}
}
👨🏫 全靠大佬题解
🍤 空间优化版
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
static int N = 1010;
static int M = 20010;
static int[] v = new int[N];
static int[] w = new int[N];
static int[] s = new int[N];
static int[] f = new int[M];// f[i][j] 前i个物品放入j容量的背包的最大价值
static int[] pre = new int[M];
static int[] q = new int[M];// 单调队列
public static void main(String[] args)
{
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();
s[i] = sc.nextInt();
}
for (int i = 1; i <= n; i++)
{
pre = Arrays.copyOf(f, pre.length);
for (int r = 0; r < v[i]; r++)//枚举余数
{
int hh = 0;
int tt = -1;
for (int j = r; j <= m; j += v[i])
{
// 队头超出滑动窗口范围
while (hh <= tt && j - q[hh] > s[i] * v[i])
hh++;
while (hh <= tt && pre[q[tt]] + (j - q[tt]) / v[i] * w[i] <= pre[j])
tt--;
q[++tt] = j;
f[j] = pre[q[hh]] + (j - q[hh]) / v[i] * w[i];
}
}
}
System.out.println(f[m]);
}
}