一、01背包
内容:有n件物品和容量为m的背包 给出i件物品的重量以及价值 求解让装入背包的物品重量不超过背包容量 且价值最大
特点:每个物品只有一件供你选择放还是不放
1. 二维解法
设f[i][j]表示前i件物品 总重量不超过j的最大价值 可得出状态转移方程 :
f[i][j]=max{f[i-1][j-w[i]]+v[i],f[i-1][j]}
w[i]:重量数组,v[i]:价值数组
代码如下:
// 0,1背包:二维法
public static int bag1(int W, int[] w, int[] v) {
int n = w.length - 1;// 第一个值,不算
int[][] f = new int[n + 1][W + 1];
for (int i = 1; i <= n; i++)
for (int j = W; j > 0; j--) {
if (w[i] <= j)
f[i][j] = Math.max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);
else
f[i][j] = f[i - 1][j];
}
return f[n][W]; // 最优解
}
缺点:数据大时,无法处理。
2.一维解法
用一维数组代替二维数组就解决了二维数组的问题
设f[j]表示重量不超过j公斤的最大价值 可得出状态转移方程 :
f[j]=max{f[j],f[j−w[i]]+v[i]}
代码如下:
// 0,1背包:一维法
public static int bag2(int W, int[] w, int[] v) {
int n = w.length - 1;// 第一个值,不算
int[] f = new int[W + 1];
for (int i = 1; i <= n; i++)
for (int j = W; j >= w[i]; j--)
if (f[j - w[i]] + v[i] > f[j])
f[j] = f[j - w[i]] + v[i];
return f[W]; // 最优解
}
二、完全背包
内容:有n件物品和容量为m的背包 给出i件物品的重量以及价值 求解让装入背包的物品重量不超过背包容量 且价值最大
特点: 每个物品可以无限选用
有二维和一维两种,但二维的有一定局限 所以只介绍一维方法
设f[j]表示重量不超过j公斤的最大价值 可得出状态转移方程 :
f[j]=max{f[j],f[j−w[i]]+v[i]}
区别:0,1背包是从大到小遍历,完全背包是从小到大遍历
代码如下:
// 完全背包:一维法
public static int bag3(int W, int[] w, int[] v) {
int n = w.length - 1;// 第一个值,不算
int[] f = new int[W + 1];
for (int i = 1; i <= n; i++)
for (int j = w[i]; j <= W; j++)
if (f[j - w[i]] + v[i] > f[j])
f[j] = f[j - w[i]] + v[i];
return f[W]; // 最优解
}
三、多重背包
内容:有n件物品和容量为m的背包 给出i件物品的重量以及价值 还有数量 求解让装入背包的物品重量不超过背包容量 且价值最大
特点:每个物品都有了一定的数量
设f[j]表示重量不超过j公斤的最大价值 可得出状态转移方程 :
f[j]=max{f[j],f[j−k∗w[i]]+k∗v[i]}
代码如下:
// 多重背包:一维法
public static int bag4(int W, int[] w, int[] v, int[] num) {
int n = w.length - 1;// 第一个值,不算
int[] f = new int[W + 1];
for (int i = 1; i <= n; i++)
for (int j = W; j >= w[i]; j--)
for (int k = 0; k <= num[i] && j - k * w[i] >= 0; k++) {
if (f[j - k * w[i]] + k * v[i] > f[j])
f[j] = f[j - k * w[i]] + k * v[i];
}
return f[W]; // 最优解
}
四、背包问题应用在,数组中找固定和的问题
查找和为100的所有组合
public static void dfs(int current, int sum, int[] questions, ArrayList<Integer> path,
HashSet<List<Integer>> result) {
if (sum == 100) {
result.add(new ArrayList<>(path));
return;
}
if (sum > 100) {
return;
}
for (int i = current; i < questions.length; i++) {
path.add(questions[i]);
dfs(i + 1, sum + questions[i], questions, path, result);
path.remove(path.size() - 1);
}
}
判断是否有和为100的组合
public static boolean findSum100(int[] a) {
boolean[] dp = new boolean[101];
dp[a[0]] = true;
for (int i = 1; i < a.length; i++) {
for (int j = 100; j >= a[i]; j--) {
if (dp[j - a[i]]) {
dp[j] = true;
}
}
if (dp[100]) {
return true;
}
}
return false;
}