分支限界法 求解0-1背包

做完才发现,分支限界并不适合做0-1背包。。。直接把一道算法题做成数据结构题了。。

众所周知啊,分支限界法的本质其实就是广度优先遍历。再通过剪枝操作,实现快速查找的目的。但是0-1背包给的两个数组,一个是货物体积,一个是货物价格。我们想要广度优先遍历,就要先构造一颗二叉树。这棵二叉树长这个样子:

每一层的节点都代表备选货物中的一个。节点中包含该货物的容量,价值信息。左子节点代表没有选该层货物,右子节点代表选了该层货物。这样就构造出了一颗类似决策树的二叉树,叶子结点代表了所有可能。

为了构建出这样一颗二叉树,我们需要把原有数组转换成这样的一颗完美二叉树。这里我们就需要引入TreeNodify函数:

public TreeNode treeNodify(int[] nums,int[] values){
        TreeNode root = new TreeNode();
        Deque<TreeNode> dq = new LinkedList<>();
        dq.addLast(root);
        Map<TreeNode,Integer> map = new HashMap<>();
        map.put(root,0);
        int cnt = nums.length;
        while(!dq.isEmpty()){
            TreeNode t = dq.poll();
            TreeNode left = new TreeNode();
            int idx = map.get(t)<cnt ? map.get(t) : 0;
            TreeNode right = new TreeNode(nums[idx],values[idx]);
            int tag = map.get(t) + 1;
            map.put(left,tag);
            map.put(right,tag);
            if(tag <= cnt){
                t.left = left;
                t.right = right;
                dq.offer(left);
                dq.offer(right);
            }
        }
        return root;
    }

这段不是中断,跳过不讲。 

但是这样全部走一遍也是很麻烦的,因此我们需要对这棵二叉树进行剪枝。怎么剪枝呢?我们都知道,当下面的节点加入背包后会撑爆背包时,我们就会跳过这个货物。因为就算加入了之后的操作也是徒劳。针对这个,我们可以以 当前节点体积 + 原有体积 是否小于背包总容量来剪枝。由于是广度优先,用队列模拟的,所以当不满足条件时,我们就不操作。这样,我们就完成了剪枝。

但是问题又来了,我们该怎样得到最大值呢? 这里我们采用在节点尾部判断最大值加入即可。因为不管最后一个选不选,两个叶子结点一定是保存了结果的,所以直接在叶子结点找值。

具体操作时,我们给每一个节点都用HashMap绑定两个值,一个是剩余空间,用来剪枝。另一个是当前最大价值。每一步都借用上一节点的这两个值便可以得到递推。

public int branchNBound(TreeNode root,int maxW){
        int ans = 0;
        int curW = 0;
        Map<TreeNode,Integer> price = new HashMap<>();
        Map<TreeNode,Integer> space = new HashMap<>();
        Deque<TreeNode> dq = new LinkedList<>();
        price.put(root,0);
        space.put(root,maxW);
        dq.addLast(root);
        while(!dq.isEmpty()){
            TreeNode treeNode = dq.poll();
            if(treeNode.left == null || treeNode.right == null){
                ans = Math.max(ans,price.get(treeNode));
                continue;
            }
            price.put(treeNode.left,price.get(treeNode));
            space.put(treeNode.left,space.get(treeNode));
            dq.offer(treeNode.left);
            curW = space.get(treeNode);
            if(curW - treeNode.right.weight >= 0){
                dq.offer(treeNode.right);
                price.put(treeNode.right,price.get(treeNode)+treeNode.right.val);
                space.put(treeNode.right,curW- treeNode.right.weight);
            }
        }
        return ans;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值