package commonlyalgorithm.dynamic;
import java.util.Arrays;
/**
* 使用动态规划算法解决背包问题
* 背包问题: 向一个能装入固定重量的背包中放入几样价值和重量都不相同东西 要求放入的东西总价值最大 求应该怎么放(每件物品只有一件 即 0-1背包)
*
* 核心思想: 用一个二维数组来表示一个表 横纵坐标分别为物品和背包可容纳重量 右下角的单元格即为最大值
* 优化思路: 填表或许可以从右往左填? 二维数组是否可以使用一行通过不断迭代的方式进行替代?
*
* 无界背包问题 公式v[i][j] = max(f[i - 1][j] , k * val[i] + v[i - 1][j - k * w[i]]) 其中k应满足 0 < k * w[i] < j
* 在集体代码中此处应为 k * val[i - 1] + v[i - 1][j - k * w[i - 1]]
*/
public class KnapsackProblem {
public static void main(String[] args) {
int[] w = {1, 4, 3}; //物品的重量
int[] val = {1500, 3000, 2000}; //物品的价值
int m = 4; //背包容量
int n = val.length;
//创建二维数组
int[][] v = new int[n + 1][m + 1];
//创建二维数组记录最大值时商品的坐标
int[][] path = new int[n + 1][m + 1];
//将二维数组的第一行和第一列置为0
for (int i = 0; i < v.length; i++) {
v[i][0] = 0;
}
for (int i = 0; i < v[0].length; i++) {
v[0][i] = 0;
}
//核心算法
for (int i = 1; i < v.length; i++) {
for (int j = 1; j < v[0].length ; j++) {
//如果当前背包的容量小于当前货物的重量,则将该单元格的值设置为上一行单元格的值
if(w[i - 1] > j){
v[i][j] = v[i - 1][j];
}else{
//若当前背包容量大于等于当前商品的值,
// 则在(上一行的值)和(当前商品值+剩余容量所能达到的最大值)之间选一个最大值作为当前单元格的值
//为了记录具体的放置方案需要在这里做出改变
//v[i][j] = Math.max(v[i - 1][j] , val[i - 1] + v[i - 1][j - w[i - 1]]);
if(v[i - 1][j] < val[i - 1]+ v[i - 1][j - w[i - 1]]){
v[i][j] = val[i - 1]+ v[i - 1][j - w[i - 1]];
path[i][j] = 1;
}else{
v[i][j] = v[i - 1][j];
}
}
}
}
//遍历数组
for (int i = 0; i < v.length; i++) {
System.out.println(Arrays.toString(v[i]));
}
//逆向遍历path数组 得到结果
int i = path.length - 1;
int j = path[0].length - 1;
while (i > 0 && j > 0){
if(path[i][j] == 1){
System.out.printf("将%d号商品放入背包中\n" , i);
//迭代j 求出背包可容纳的剩余重量
j -= w[i - 1];
}
i--;
}
}
}