注意:以下部分内容摘自Acwing,仅用于个人学习记录,不做任何商业用途。
(1)01背包问题
原链接:AcWing 2. 01背包问题(状态转移方程讲解) - AcWing (讲得很好,我搬运过来是为了自己学习用,不做任何商业用途噢~)
题目描述: 有 N 件物品和一个容量为 V 的背包,每件物品有各自的价值且只能被选择一次,要求在有限的背包容量下,装入的物品总价值最大。
代码:
for(int i = 1; i <= n; i++)
for(int j = m; j >= 0; j--)
{
if(j < v[i])
f[i][j] = f[i - 1][j]; // 优化前
f[j] = f[j]; // 优化后,该行自动成立,可省略。
else
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]); // 优化后
}
作者:深蓝
链接:https://www.acwing.com/solution/content/1374/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
实际上,只有当枚举的背包容量 >= v[i] 时才会更新状态,因此我们可以修改循环终止条件进一步优化。
for(int i = 1; i <= n; i++)
{
for(int j = m; j >= v[i]; j--)
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
(2)完全背包问题
与01背包问题的区别:每样物品可以选无数件:
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i ++ )
cin >> v[i] >> w[i];
for(int i = 0; i <= m; i++)//初始化
{
f[0][i] = 0;
}
for(int i = 1; i <= n; i ++ )
for(int j = 0; j <= m; j ++ )
for(int k = 0; k * v[i] <= j; k ++ )
f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
//求出每一个 f[i][j]
cout << f[n][m] << endl;
优化:
转自acwing:AcWing 3. 完全背包问题 - AcWing
(3) 多重背包问题
问题描述:第 i 种物品最多有 si 件,其他同上。
传统方法:
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++)
for(int k=0;k<=s[i]&&k*v[i]<=j;k++){
f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+k*w[i]);
}
}
创新:把s个相同物品拆成s个不同的,转换成01背包
当数据过大时,我们需要使用二进制优化:
如果个数为10,那么可以用1+2+4+ 3来表示,这样10个物品就可以被分成4个物品了,这样的话就是logN的复杂度,优化了很多。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
static final int N=200002;
//si * 每个si能拆出来的个数 + 左右预留的哨兵位置 = 2000 * log2(2000) + 2 <= 22002
static int[] v =new int[N];//体积
static int[] w =new int[N];//价值
static int[] f =new int[N];//f存储最大价值
public static void main(String[] args) throws IOException {
BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
int n,V;
String[] strings=br.readLine().split(" ");
n=Integer.parseInt(strings[0]);
V=Integer.parseInt(strings[1]);
int idx=1;
for(int i=0;i<n;i++) {
int si,vi,wi;
strings=br.readLine().split(" ");
vi=Integer.parseInt(strings[0]);
wi=Integer.parseInt(strings[1]);
si=Integer.parseInt(strings[2]);
int k=1;
while(si>k) {
v[idx]=vi*k;
w[idx]=wi*k;
si-=k;
k*=2;
idx++;
}
if(si>0) {
v[idx]=si*vi;
w[idx]=si*wi;
idx++;
}
}
for(int i=1;i<idx;i++) {
for(int m=V;m>=v[i];m--)
f[m]=Math.max(f[m], f[m-v[i]]+w[i]);
}
System.out.print(f[V]);
}
}