动态规划解决0/1背包问题(Java实现)
写在前面
首先感谢@changyuanchn分享的动态规划算法。本文为Java实现版本。对动态规划不了解的同学建议先阅读以下两篇文章。
首先建立物品DPItem物品类
public class DPItem {
private String name;//名字
private int value;//价值
private int weight;//重量
public DPItem(String name, int value, int weight) {
this.name = name;
this.value = value;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "DPItem{" +
"name='" + name + '\'' +
", value=" + value +
", weight=" + weight +
'}';
}
}
我们先实现递归版本,动态规划就是在递归版本上加入缓存而已
/**
* 测试方法
*/
private void test() {
//init data
List<DPItem> items = new ArrayList<>();
String[] names = new String[]{"A", "B", "C", "D", "E", "F", "G"};
int[] values = new int[]{35, 30, 6, 50, 40, 10, 25};
int[] weights = new int[]{10, 40, 30, 50, 35, 40, 30};
for (int i = 0; i < names.length; i++) {
DPItem item = new DPItem(names[i], values[i], weights[i]);
items.add(item);
}
long timeMillis = System.currentTimeMillis();
Log.e("DynamicProgramming", "put items into package start!");
List<DPItem> packageItems = maxValue(items, 150);
for (DPItem item : packageItems) {
Log.e("DynamicProgramming", "put \'" + item.toString() + "\' into package");
}
Log.e("DynamicProgramming", "The maximum value in the package is " + valueSum(packageItems));
Log.e("DynamicProgramming", "put items into package end! Total time is " + (System.currentTimeMillis() - timeMillis) + " ms");
}
/**
* 给定源物品集合及背包剩余的重量,获取背包所能装下的最大价值
*
* @param sourceItems 源物品数组
* @param leftRoom 背包剩余的重量
* @return 最大价值
*/
private List<DPItem> maxValue(List<DPItem> sourceItems, int leftRoom) {
//先判断结束条件:1、源物品被装完了,size为零。2、剩余重量为0,再也装不下物品。
if (sourceItems.size() == 0 || leftRoom == 0) {
return new ArrayList<>();
} else {
DPItem firstItem = sourceItems.get(0);
if (firstItem.getWeight() > leftRoom) {
return maxValue(sourceItems.subList(1, sourceItems.size()), leftRoom);
} else {
//添加进背包内
List<DPItem> itemsLeft = new ArrayList<>();
itemsLeft.add(firstItem);
itemsLeft.addAll(maxValue(sourceItems.subList(1, sourceItems.size()), leftRoom - firstItem.getWeight()));
int valueSumLeft = valueSum(itemsLeft);
//不放进包内
List<DPItem> itemsRight = maxValue(sourceItems.subList(1, sourceItems.size()), leftRoom);
int valueSumRight = valueSum(itemsRight);
return valueSumLeft > valueSumRight ? itemsLeft:itemsRight;
}
}
}
/**
* 计算物品总价值
* @param items 物品集合
* @return 总价值
*/
private int valueSum(List<DPItem> items) {
int value = 0;
for(DPItem item: items) {
value += item.getValue();
}
return value;
}
输出结果:
2019-12-30 01:13:28.700 5747-5747/com.huangxin.assemble E/DynamicProgramming: put items into package start!
2019-12-30 01:13:28.704 5747-5747/com.huangxin.assemble E/DynamicProgramming: put 'DPItem{name='A', value=35, weight=10}' into package
2019-12-30 01:13:28.704 5747-5747/com.huangxin.assemble E/DynamicProgramming: put 'DPItem{name='B', value=30, weight=40}' into package
2019-12-30 01:13:28.704 5747-5747/com.huangxin.assemble E/DynamicProgramming: put 'DPItem{name='D', value=50, weight=50}' into package
2019-12-30 01:13:28.704 5747-5747/com.huangxin.assemble E/DynamicProgramming: put 'DPItem{name='E', value=40, weight=35}' into package
2019-12-30 01:13:28.704 5747-5747/com.huangxin.assemble E/DynamicProgramming: The maximum value in the package is 155
2019-12-30 01:13:28.705 5747-5747/com.huangxin.assemble E/DynamicProgramming: put items into package end! Total time is 6 ms
最后我们根据动态规划来优化maxValue方法:
private Map<String, List<DPItem>> cache = new HashMap<>();
/**
* 给定源物品集合及背包剩余的重量,获取背包所能装下的最大价值
*
* @param sourceItems 源物品数组
* @param leftRoom 背包剩余的重量
* @return 最大价值
*/
@SuppressLint("DefaultLocale")
private List<DPItem> maxValue(List<DPItem> sourceItems, int leftRoom) {
//如果有缓存,直接使用缓存
List<DPItem> cacheItems = cache.get(String.format("%d_%d",sourceItems.size(), leftRoom));
if (cacheItems != null) {
return cacheItems;
}
//先判断结束条件:1、源物品被装完了,size为零。2、剩余重量为0,再也装不下物品。
if (sourceItems.size() == 0 || leftRoom == 0) {
return new ArrayList<>();
} else {
DPItem firstItem = sourceItems.get(0);
if (firstItem.getWeight() > leftRoom) {
return maxValue(sourceItems.subList(1, sourceItems.size()), leftRoom);
} else {
//添加进背包内
List<DPItem> itemsLeft = new ArrayList<>();
itemsLeft.add(firstItem);
List<DPItem> resultLeft = maxValue(sourceItems.subList(1, sourceItems.size()), leftRoom - firstItem.getWeight());
itemsLeft.addAll(resultLeft);
int valueSumLeft = valueSum(itemsLeft);
//不放进包内
List<DPItem> itemsRight = maxValue(sourceItems.subList(1, sourceItems.size()), leftRoom);
int valueSumRight = valueSum(itemsRight);
List<DPItem> result = valueSumLeft > valueSumRight ? itemsLeft:itemsRight;
//缓存结果
cache.put(String.format("%d_%d",sourceItems.size(), leftRoom), result);
return result;
}
}
}