(待补全完整)
01背包
问题描述
有n件物品,每件物品的重量为w[i],价值为c[i]。现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的价值最大。其中每种物品都有1件。
样例输入
5 8 // n == 5, V == 8
3 5 1 2 2 //w[i] 重量
4 5 2 1 3 //c[i] 价值
结果为 10
代码
package 背包问题;
import java.util.Scanner;
public class 零一背包 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int V = in.nextInt();
int[] w = new int[n];
int[] c = new int[n];
int[] dp = new int[V+1];
//输入一组物品的重量
for(int i = 0; i < n; i++){
w[i] = in.nextInt();
}
//输入一组物品的价值
for(int i = 0; i < n; i++){
c[i] = in.nextInt();
}
int maxn = 0;
//核心过程
for(int i = 0; i < n; i++){
for(int v = V; v >= w[i]; v--){
dp[v] = Math.max(dp[v],dp[v - w[i]] + c[i]);
maxn = Math.max(dp[v],maxn);
}
}
System.out.println(maxn);
}
}
完全背包
问题描述
有n种物品,每种物品的单件重量为w[i],价值为c[i]。现在有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的总价值最大。其中每种物品都有无穷件。
这里优化之后与01背包其实很是相似,原理不多讲。主要是记录一下模版
代码
package 背包问题;
import java.util.Scanner;
public class 完全背包 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int V = in.nextInt();
int[] w = new int[n];
int[] c = new int[n];
int[] dp = new int[V+1];
//输入一组物品的重量
for(int i = 0; i < n; i++){
w[i] = in.nextInt();
}
//输入一组物品的价值
for(int i = 0; i < n; i++){
c[i] = in.nextInt();
}
int maxn = 0;
for(int i = 0; i < n; i++){
//与01背包相比多了逆序变成了正序的变化
for(int v = w[i]; v <= V; v++){
dp[v] = Math.max(dp[v],dp[v - w[i]] + c[i]);
maxn = Math.max(dp[v],maxn);
}
}
System.out.println(maxn);
}
}
多重背包
问题描述
有n种物品,每种物品的重量为w[i],价值为c[i],数量为s[i]。现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的价值最大。与01背包的差距就是多了数量的限制。
这里呢,是最初始的模版,也就是直接三重for循环,通过o1背包加了一层循环来写的,直接添加了对数量的限制。
代码
package 背包问题;
import java.util.Scanner;
public class 多重背包 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int V = in.nextInt();
int[] w = new int[n];
int[] c = new int[n];
int[] s = new int[n];
int[] dp = new int[V + 1];
//输入一组物品的重量
for (int i = 0; i < n; i++) {
w[i] = in.nextInt();
}
//输入一组物品的价值
for (int i = 0; i < n; i++) {
c[i] = in.nextInt();
}
//输入一组物品的数量
for (int i = 0; i < n; i++) {
s[i] = in.nextInt();
}
//三重循环来进行背包填充
for (int i = 0; i < n; i++) {
for (int j = V; j >= w[i]; j--){
for(int k = 0;k <= s[i] && k * w[i] <= j; k++){
dp[j] = Math.max(dp[j],dp[j - k * w[i]] + k * c[i]);
}
}
}
System.out.println(dp[V]);
}
}
多重背包优化
优化描述
问题同上。上面我们虽然解决了多重背包的问题,但是问题也出现了,我们为了加一个限制,于是乎又加了一重循环,导致复杂度的增加,但是我们可以进行优化。
具体思想就是将多重背包压缩成01背包,拆分s变成s组,可以将1-s的数量与价值进行绑定,相当于变成了01背包。但是这样做的拆分复杂度还是高,所以具体就是用到了二进制优化的思想。
代码
package 背包问题;
import java.util.Scanner;
public class 多重背包优化 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int V = in.nextInt();
int[] w = new int[n];
int[] c = new int[n];
int[] s = new int[n];
int[] dp = new int[V + 1];
//具体输入要求视题目而定可以优化代码
//输入一组物品的重量
for (int i = 0; i < n; i++) {
w[i] = in.nextInt();
}
//输入一组物品的价值
for (int i = 0; i < n; i++) {
c[i] = in.nextInt();
}
//输入一组物品的数量
for (int i = 0; i < n; i++) {
s[i] = in.nextInt();
}
//我们需要对背包进行压缩
int[] newW = new int[5000]; //压缩后的新重量,这里尽量开一个比较大的值,视题目要求
int[] newC = new int[5000]; //压缩后的价值,这里尽量开一个比较大的值,视题目要求
int cnt = 0; // 用来统计我们绑定后数组大小
for(int i = 0; i < n; i++){
for(int k = 1; k <= s[i]; k *= 2){
s[i] -= k;
newW[cnt] = w[i] * k;
newC[cnt] = c[i] * k;
cnt++;
}
//如果s[i]还有剩余继续添加
if(s[i] > 0){
newW[cnt] = w[i] * s[i];
newC[cnt] = c[i] * s[i];
cnt++;
}
}
//然后就是套用我们的01背包模版
for(int i = 0; i < cnt; i++){
for(int j = V; j >= newW[i]; j--){
dp[j] = Math.max(dp[j],dp[j - newW[i]] + newC[i]);
}
}
System.out.println(dp[V]);
}
}
分组背包
题目描述
有 NN 组物品和一个容量是 VV 的背包。每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
这里我们改一下输入格式,我们以如下的输入格式进行代码的编写:
代码
package 背包问题;
import java.util.Scanner;
public class 分组背包 {
static int N = 110; //具体范围视题目要求
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int V = in.nextInt();
int[][] w = new int[N][N];
int[][] c = new int[N][N];
int[] s = new int[n];
int[] dp = new int[N];
for(int i = 0; i < n; i++){
//每一组背包有多少的数量
s[i] = in.nextInt();
for(int j = 0; j < s[i]; j++){
w[i][j] = in.nextInt();
c[i][j] = in.nextInt();
}
}
for(int i = 0; i < n; i++){
for(int j = V; j >= 0; j--){
for(int k = 0; k < s[i]; k++){
if(j >= w[i][k]){ //如果我们的背包可以容纳当前组的可选背包的容量
dp[j] = Math.max(dp[j],dp[j - w[i][k]] + c[i][k]);
}
}
}
}
System.out.println(dp[V]);
}
}
混合背包
题目描述
解题思路
我们可以进行一个判断,如果是-1进行01背包处理,如果是0进行完全背包处理,至于多重背包,上面也说过了,可以用二进制优化,也加入到01背包里面。这里我们可以用一个背包类来进行存储,然后实现代码,模版如下。
代码
package 背包问题;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class 混合背包 {
static class Bag{
int kind; //背包种类
int w; //背包重量或者体积之类的限制
int c; //背包价值
Bag(int kind,int w,int c){
this.kind = kind;
this.w = w;
this.c = c;
}
}
static int N = 1010; //具体大小视题目而定
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
List<Bag> bags = new ArrayList<>();
int n = in.nextInt();
int V = in.nextInt();
int[] dp = new int[N];
for(int i = 0; i < n; i++){
int w = in.nextInt();
int c = in.nextInt();
int s = in.nextInt();
if(s < 1){
bags.add(new Bag(-1,w,c));
}else if(s == 0){
bags.add(new Bag(0,w,c));
}else{
//如果是多重背包则进行二进制优化
for(int j = 1; j <= s; j *= 2){
s -= j;
bags.add(new Bag(-1,w * j,c * j));
}
if(s > 0) bags.add(new Bag(-1,w * s,c * s));
}
}
//然后就是分成了01背包和完全背包来处理
for(Bag b : bags){
if(b.kind == -1) {
for(int j = V; j >= b.w; j--){
dp[j] = Math.max(dp[j],dp[j - b.w] + b.c);
}
}else{
for(int j = 0; j <= V; j++){
dp[j] = Math.max(dp[j],dp[j - b.w] + b.c);
}
}
}
System.out.println(dp[V]);
}
}
二维费用背包
二维费用背包,其实很好理解,就是原本我们背包只有一个重量的限制,现在可能多了一个体积的限制。
相比一维的01背包其实就多了一重循环,同时我们也可以继续扩展问题,三维费用背包,四维费用背包等等。
代码
package 背包问题;
import java.util.Scanner;
public class 二维费用背包 {
static int N = 1010; //视题目要求进行修改范围
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int V = in.nextInt();
int M = in.nextInt();
int[][] dp = new int[N][N];
for(int i = 0; i < n; i++){
int v = in.nextInt(); //物品体积
int m = in.nextInt(); //物品重量
int c = in.nextInt(); //物品价值
for(int j = V; j >= v; j--){
for(int k = M; k >= m; k--){
dp[j][k] = Math.max(dp[j][k],dp[j - v][k - m] + c);
}
}
}
System.out.println(dp[V][M]);
}
}
背包问题方案数
求背包问题的方案
依赖背包问题
参考资料
《算法笔试》
《背包九讲》