0
点赞
收藏
分享

微信扫一扫

动态规划之用Java实现01背包

前端王祖蓝 2022-01-24 阅读 51

背包问题有很多种,是最简单的动态规划类型。

01背包

描述: 有 N 件物品和一个容量为 V 的背包。(每种物品均只有一件)第 i 件物品 的费用是 c[i],价值是 w[i]。求解将哪些物品装入背包可使价值总和最大。

解析:N件物品可以看做是有N种备选方案,容量为V的背包可以看做是成本为V,c[i]是每种方案的成本,价值是每种方案带来的效益。那么问题就变成了,在不超过总成本V的情况下,使用哪些备选方案,可以使效益最大。


不过,这里针对的是单一物品特性,如果一个复杂系统是多个组成部分,每个部分有多种不同的解决方案,然后成本和效能不一样,就不能使用简单01背包,不过它的每个部分是一个简单01背包。

首先定义物品节点

public class Thing {

    private String name;
    private Integer number;
    private Integer weight;
    private Integer income;
    ... ... 
}

然后代码实现为:

import java.util.*;

public class BackPackUtil {

    public static Set<Thing> backPack01(List<Thing> things, Integer totalCost) {
        Map<Integer, Set<Thing>> ans = new HashMap<>();
        int n = things.size();
        int[] f = new int[totalCost+1];
        for (int i = 0; i < n; i++) {
            for (int j = totalCost; j >= things.get(i).getWeight(); j--) {
                if ((f[j - things.get(i).getWeight()] + things.get(i).getIncome()) > f[j]) {
                    f[j] = f[j - things.get(i).getWeight()] + things.get(i).getIncome();
                    if (!ans.containsKey(j)) {
                        ans.put(j, new HashSet<>());
                    } else {
                        ans.get(j).clear();
                    }
                    if (!ans.containsKey(j - things.get(i).getWeight())) {
                        ans.put(j - things.get(i).getWeight(), new HashSet<>());
                    }
                    ans.get(j).addAll(ans.get(j - things.get(i).getWeight()));
                    ans.get(j).add(things.get(i));
                }
            }
        }
        return ans.get(totalCost);
    }

    public static void main(String[] args) {
        String[][] a = {{"01-1","1","2","3"},{"01-2","1","4","7"},{"01-3","1","8","15"},{"01-4","1","16","31"}};
        List<Thing> things = new ArrayList<>();
        for (String[] b : a){
            Thing thing = new Thing();
            thing.setName(b[0]);
            thing.setNumber(Integer.parseInt(b[1]));
            thing.setWeight(Integer.parseInt(b[2]));
            thing.setIncome(Integer.parseInt(b[3]));
            things.add(thing);
        }
        for (int i=10;i<30;i++){
            Set<Thing> solveMethod = backPack01(things,i);
            StringBuffer thingss = new StringBuffer();
            int totalValue = 0;
            int totalWeight = 0;
            for (Thing thing : solveMethod){
                thingss.append(thing.getName()).append(" ").append(thing.getWeight()).append(" ").append(thing.getIncome()).append("\n");
                totalValue = totalValue + thing.getIncome();
                totalWeight += thing.getWeight();
            }
            System.out.println("计划成本为:"+i+" 总消耗为:"+totalWeight +" 总收益为:"+totalValue);
            System.out.print(thingss.toString());
        }
    }
}

有的人可能会说了,这个问题我用贪心算法也可以解决,思路就是计算每种物品的性价比,然后根据性价比从大到小排序放到背包中。你可以朝着这个思路尝试一下,看下会出现什么问题,如果你发现确实更快,或者你发现的问题,都可以在评论区和我交流。

我们现在来分析分析这里的算法核心

public static Set<Thing> backPack01(List<Thing> things, Integer totalCost) {
        Map<Integer, Set<Thing>> ans = new HashMap<>();
        int n = things.size();
        int[] f = new int[totalCost+1];
        for (int i = 0; i < n; i++) {
            for (int j = totalCost; j >= things.get(i).getWeight(); j--) {
                if ((f[j - things.get(i).getWeight()] + things.get(i).getIncome()) > f[j]) {
                    f[j] = f[j - things.get(i).getWeight()] + things.get(i).getIncome();
                    ... ...
                }
            }
        }
        ... ...
    }

核心算法实现在这两重循环,第一重循环的意图是枚举每一个物品,第二重循环的意图是如果算上当前这种物品,确定每种不同的成本的结果。那么显而易见,最终的结果就是遍历完所有物品,消耗为totalCost的结果。

上面的main函数里面的测试代码,可以进行剪枝,直接调用一次出所有的结果。如果你感兴趣,可以尝试一下。


举报

相关推荐

0 条评论