动态规划解决0/1背包问题(Java实现)

动态规划解决0/1背包问题(Java实现)

写在前面

首先感谢@changyuanchn分享的动态规划算法。本文为Java实现版本。对动态规划不了解的同学建议先阅读以下两篇文章。

动态规划
动态规划解决0/1背包问题

首先建立物品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;
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值