回溯法:0-1背包问题

本文介绍了如何运用回溯法和递归策略解决背包问题,以物品重量、价值和背包容量为输入,寻找总价值最大的物品组合。通过构建子集树和设置终止条件,实现了0/1背包问题的求解代码。
摘要由CSDN通过智能技术生成

问题描述

给定n种物品和一背包。 物品i的重量是w_i, 其价值为v_i,背包的容量为 c。 问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?注意物品不重复!

实例:物品价值V={12, 11, 9, 8}, 物品重量W={8, 6, 4, 3}, 背包容量c=13

结点:向量(x_1,x_2,...,x_k) ( 子集的部分特征向量)

搜索空间: 子集树2^n片树叶

其中两个可行解为:

<0,1,1,1>:x_1=0,x_2=1,x_3=1,x_4=1\\ <1,0,1,0>:x_1=1,x_2=0,x_3=1,x_4=0

回溯法模版回顾 

参考文章:代码随想录

回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!

因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度

递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

实现代码

终止条件代码

public static void backtracking(int n,  int startIndex) {
    if (startIndex>=n){
        //此时startIndex越界了
        if (getPathSum()<=c){
            result.add(new ArrayList<>(path));
            return;
        }
        return;
    }
    //再加后面任意一个就肯定不够了
    if (getPathSum()<=c&&(getPathSum() + items_min_weight[startIndex]) > c) {
        //        if (getPathSum()<c) {
        result.add(new ArrayList<>(path));
        return;
    }
    for (int i = startIndex; i < n; i++) {
        path.add(i);
        backtracking(n,  i + 1);
        path.removeLast();
    }

最终代码(含注释)

需要注意的是这里的可行,是再加上未选中的任意一项就>背包容量C

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class KnapsackProblem {
    static List<List<Integer>> result = new ArrayList<>();
    static LinkedList<Integer> path = new LinkedList<>();

    static int N = 4;
    //    static int[] items_weight = new int[N];
    static int[] items_weight = {8, 6, 4, 3};
    //    static int[] items_value = new int[N];
    static int[] items_value = {12, 11, 9, 8};
    //每个items_min_weight(对应下标为i)的值为min{items_weight[i],...,items_weight[N-1]}
    static int[] items_min_weight = new int[N];
    //c为背包的容量
    static int c=13;

    public static void main(String[] args) {
        items_min_weight[N - 1] = items_weight[N - 1];
        int min = items_min_weight[N - 1];
        for (int i = items_weight.length - 2; i >= 0; i--) {
            if (items_weight[i] < min) {
                min = items_weight[i];
            }
            items_min_weight[i] = min;
        }
        backtracking(N,  0);
        System.out.println("可行解有:");
        result.forEach(System.out::println);
        //要是想求最优解,直接对每个可行解对应重量求和,之后取最大一个就好啦
    }

    public static void backtracking(int n,  int startIndex) {
        if (startIndex>=n){
            //此时startIndex越界了
            if (getPathSum()<=c){
                result.add(new ArrayList<>(path));
                return;
            }
            return;
        }
        //再加后面任意一个就肯定不够了
        if (getPathSum()<=c&&(getPathSum() + items_min_weight[startIndex]) > c) {
            //        if (getPathSum()<c) {
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i < n; i++) {
            path.add(i);
            backtracking(n,  i + 1);
            path.removeLast();
        }
    }

    public static int getPathSum() {
        int sum = 0;
        for (int i = 0; i < path.size(); i++) {
            sum += items_weight[path.get(i)];
        }
        return sum;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十有久诚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值