背包类型有三种:
- 01背包模型:有N种物品和一个容量为V的背包,每种物品只有一种。第i种物品的体积是v[i],价值是w[i]。求不超过体积且价值最大。
- 完全背包模型:有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是v[i],价值是w[i]。求不超过体积且价值最大。
- 多重背包模型:有N种物品和一个容量为V的背包,每种物品都有有限件可用。第i种物品的体积是v[i],价值是w[i],最多可用件数为n[i]。求不超过体积且价值最大。
其中,01和多重为了保证,i层由i-1层递推过来,j(背包限制)是从大到小遍历的。
原因:从较小体积→较大体积有可能出错
想象循环到第i行,从左向右更新j的状态,当更新某个j的时候用到了j-w,j-w其实已经在第i行被更新过了(因为顺序是从左往右更新),但我们需要的是第i-1行j-w的状态。
总而言之,我们需要的j是第i层的,而不是i-1层的j,因此需要逆序。
代码逻辑:
- 遍历从前i个的方案——第一层for
- 对于每一个i,遍历每一种体积(此处应有限制,j必须不小于i-1层的体积,且不超过最大体积)——第二层for
(3. 对于每一种2.,遍历选取多少个的可能)
多重一维优化(节选自采药P1048)
for (int i = 0; i < n; i ++ )
{
int v, w;
cin >> v >> w;
for (int j = m; j >= v; j -- )
f[j] = max(f[j], f[j - v] + w);
}
01背包一维优化
for(int i = 1; i <= n; i++)
for(int j = m; j >= 0; j--)
{
f[i][j] = f[i - 1][j]; // 优化前
f[j] = f[j]; // 优化后,该行自动成立,可省略。
if(j >= v[i])
f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]); // 优化前
f[j] = max(f[j], f[j - v[i]] + w[i]); // 优化后
}
采药P1048
01背包问题,板子。
这里提供优化和没有优化的代码
import java.io.*;
public class P1048 {
static int T=1000+10;
static int M=100+10;
static int t,m;
static int f[][]=new int[M][T];
static int ff[]=new int[T];
static int time[]=new int[M];
static int w[]=new int[M];
public static void main(String args[]) throws IOException{
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
String init[]=in.readLine().split(" ");
t=Integer.parseInt(init[0]);
m=Integer.parseInt(init[1]);
for(int i=1;i<=m;i++) {//*
String data[]=in.readLine().split(" ");
time[i]=Integer.parseInt(data[0]);
w[i]=Integer.parseInt(data[1]);
for(int j=t;j>=time[i];j--) {
ff[j]=Math.max(ff[j],ff[j-time[i]]+w[i]);
}
}
System.out.println(ff[t]);
for(int i=1;i<=m;i++) {
for(int j=0;j<=t;j++) {
f[i][j]=f[i-1][j];
if (j >= time[i]) f[i][j] = Math.max(f[i][j], f[i - 1][j - time[i]] + w[i]);//选
}
}
System.out.println(f[m][t]);
}
}
装箱问题P1049
01背包问题,板子题。
本题特殊点:需要转换问题,将“求箱子剩余空间最小”→“装入物品体积最大”。
import java.util.*;
public class P1049 {
static int V=20000+10;
static int N=30+10;
static int boxV,n;
static int f[]=new int[N];
static int v[]=new int[N];
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
boxV=sc.nextInt();
n=sc.nextInt();
for(int i=1;i<=n;i++) {
int v=sc.nextInt();
for(int j=boxV;j>=v;j--) {
f[j]=Math.max(f[j],f[j-v]+v);
}
}
// for(int i=1;i<=n;i++) {
// f[i]=f[i-1];
//
// if(f[i-1]-v[i]>=0) f[i]=Math.min(f[i],f[i-1]-v[i]);
// }
//
System.out.println(boxV-f[boxV]);
}
}
摆花P1077
多重背包问题。
初始化:从前0种中选,自然只有1种方案
import java.util.*;
public class P1077 {
static int n,m;
static int N=100+10;
static int f[]=new int[N];
static int mod=1000007;
public static void main(String args[]) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
f[0]=1;
for(int i=0;i<n;i++) {
int a=sc.nextInt();
for(int j=m;j>=0;j--) {//遍历容量,遍历摆多少个
for(int k=1;k<=j && k<=a;k++) {//遍历选i-1号花盆的情况
f[j]=(f[j]+f[j-k])%mod;
}
}
}
System.out.println(f[m]);
}
}
买书AcWing1023
将个数遍历到小于等于j
代码具体查看:https://www.acwing.com/solution/content/52967/