动态规划合集:
1.矩阵链乘法
2.投资组合问题
3.完全背包问题
4.01背包问题
5.最长公共子序列
例题3——背包问题
描述
一个背包,可以放入n种物品,物品j的重量和价值分别为,如果背包的最大重量限制是b,怎么样选择放入背包的物品以使得背包的总价值最大?
组合优化问题,设表示装入背包的第j个物品的数量,解可以表示为
。那么目标函数和约束条件是:
如果组合优化问题的目标函数和约束条件都是线性函数,称为线性规划。如果线性规划问题的变量都是非负整数,则称为整数规划问题。背包问题就是整数规划问题。限制所有的
时称为0-1背包
子问题的界定(就是靠什么来划分子问题):由参数k和y界定
k:考虑对物品1,2,3,...,k的选择。
y:表示背包总重
子问题计算顺序:k=1,2,...,k,对给定的k,y=1,2,...,b
:装前k个物品,重量不超过y时的背包最大值。
包含两种情况:不装第k种物品或至少装一件第k种物品。
对的解释:装一件第k种物品后,最优的解法仍然是在前k个物品进行选择,仍有可能再选入1件第k种物品。
对边界条件:
:即只用第一种物品背包重量限制为y的最大价值,为了保证背包不超重,第一种物品至多能装
个,因为背包价值为
有些
那么通过设置为负无穷,在选择过程中抛弃掉为负的情况。
标记函数:用来追踪解
实例
按照递推公式:以k=2为例子,简单演算如下:
依次类推,可得备忘录表:

标记函数的备忘录:

关于背包问题的总结
物品受限背包:第i种物品最多用个
0-1背包问题:
多背包:m个背包,背包装入最大重量
在满足所有背包重量约束下使物品价值最大。
二维背包:每件物品重量和体积
,背包总重不超过b,体积不超过V,使得物品价值最大。
代码实现
此问题是完全背包问题,即 一个物品可重复出现。
public class knapsackProblem {
public static int[][]mem; // 备忘录表
public static int[][]s; // 标记函数表
public static void main(String[] args) {
int n = 4;
int d = 10;
int []w = {2,3,4,7};
int []v = {1,3,5,9};
mem = new int[n+1][d+1];
s = new int[n+1][d+1];
// 默认初始化为0
int max_value = Completely_backpack(w,v,n,d);
System.out.printf("背包最大值为:%d\n",max_value);
System.out.printf("备忘录表为:\n");
for (int i = 0; i < n + 1; i++) {
for (int j = 0; j < d + 1; j++) {
System.out.printf("%d ",mem[i][j]);
}
System.out.println();
}
System.out.println("标记函数表尾:");
for (int i = 0; i < n + 1; i++) {
for (int j = 0; j < d + 1; j++) {
System.out.printf("%d ",s[i][j]);
}
System.out.println();
}
// 追踪解 且 初始化为 0
int []res = new int[n+1];
traceSolution(res,n,d,w);
System.out.println("背包装入各个物品的数量为:");
for (int i = 1; i < n + 1; i++) {
System.out.printf("%d ",res[i]);
}
}
public static int Completely_backpack(int []w,int []v,int n,int d){
// F_k(y) = max{F_{k-1}(y), F_k(y-w_k)+v_k }
// i表示 前i个 物品放入背包
for (int i = 1; i <= n; i++) {
// j 表示 背包重量为j
for (int j = 1; j <= d; j++) {
int not = mem[i-1][j];
// w[i-1]是因为 w下标从0 开始,而i从1开始
int in;
if (j-w[i-1] < 0){
in = Integer.MIN_VALUE;
}
else in = mem[i][j-w[i-1]] + v[i-1];
mem[i][j] = Math.max(not,in);
// 根据标记函数的定义来写
if (not > in){
s[i][j] = s[i-1][j];
}
else{
s[i][j] = i;
}
}
}
return mem[n][d];
}
public static void traceSolution(int []res,int n,int d,int []w){
int y = d;
for (int i = n; i >0 ;) {
int temp = s[i][y];
while(temp == i){
// i-1 符合w的下标
y = y-w[i-1];
res[i]++;
temp = s[i][y];
}
i = s[i][y];
}
}
}
网友评论