背包问题算法实现(全组合、暴力递归、动态规划及空间压缩、对数器)

背包问题的实现
* 1.全组合解法(对数器)
* 2.暴力递归解法
* 3.动态规划解法
* 4.动态规划(省空间)解法

对数器的思路是:求解n个物品的全组合中不超过背包容量的组合对应的最大价值(时间复杂度极高:   {\color{Blue} O(n\ast 2^{n})}   )

package com.logbug.algorithm.knapsack;

import lombok.Data;

import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 背包问题的实现
 * 1.全组合解法(对数器)
 * 2.暴力递归解法
 * 3.动态规划解法
 * 4.动态规划(省空间)解法
 *
 * @author : lin.chen1
 * @version : 1.0.0.0
 */
@Data
public class KnapsackRecursion {
    /**
     * 背包容量
     */
    int c;
    /**
     * 重量数组
     */
    int[] w;
    /**
     * 价值数组
     */
    int[] v;

    public static void main(String[] args) {
        debug();
        test();
    }

    static void debug() {
        // 固定参数用于调试
        int[] w = new int[]{1, 3, 5, 7};
        int[] v = new int[]{2, 5, 9, 15};
        int c = 14;
        commonTest(w, v, c);
    }

    static void test() {

        int testTimes = 1000 * 1000;
        int num = 10;
        int wv = 100;

        for (int i = 0; i < testTimes; i++) {
            int[] w = new int[num];
            int[] v = new int[num];
            // 保证非0
            int c = (int) (Math.random() * wv) + 1;
            for (int j = 0; j < num; j++) {
                w[j] = (int) (Math.random() * wv + 1);
                v[j] = (int) (Math.random() * wv + 1);
            }
            commonTest(w, v, c);
        }
        System.out.println("success");
    }

    static void commonTest(int[] w, int[] v, int c) {
        System.out.println("w = " + Arrays.stream(w).boxed().map(Objects::toString).collect(Collectors.joining(",")));
        System.out.println("v = " + Arrays.stream(v).boxed().map(Objects::toString).collect(Collectors.joining(",")));
        System.out.println("c = " + c);
        KnapsackRecursion knapsackRecursion = new KnapsackRecursion();
        knapsackRecursion.setW(w);
        knapsackRecursion.setV(v);
        knapsackRecursion.setC(c);
        // 对数器解法
        int result1 = knapsackRecursion.simple();
        // 暴力递归解法
        int result2 = knapsackRecursion.forceRecursion(w.length, c);
        // 动态规划解法
        int result3 = knapsackRecursion.dynamicPrograming();
        // 动态规划省空间解法
        int result4 = knapsackRecursion.dynamicProgramingPro();
        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
        System.out.println("result3 = " + result3);
        System.out.println("result4 = " + result4);
        if (result1 != result2 || result1 != result3 || result1 != result4) {
            System.err.println("fail");
            System.exit(0);
        } else {
            System.out.println("=====================");
        }
    }

    /**
     * 对数器
     */
    int simple() {
        int max = 0;
        // n 个物品的全组合
        for (int num = 1; num < 1 << w.length; num++) {
            // 选中物品的总重量
            int allW = 0;
            // 选中物品的总价值
            int allV = 0;
            // 第num中情况的背包价值;
            for (int i = 0; i < w.length; i++) {
                if ((1 << i & num) != 0) {
                    allV += v[i];
                    allW += w[i];
                }
            }
            // 未超出背包容量的组合选的总重量与当前最大值比较
            if (allW <= c) {
                max = Math.max(max, allV);
            }
        }
        return max;
    }

    /**
     * 暴力递归版本
     * 返回扫过第n个元素后背包的最大价值。<br>
     * 第n个元素的结果依赖第n-1个元素的结果,从n开始递归
     *
     * @param n     元素位置
     * @param wight 来到第n个元素时背包的剩余容量
     * @return 对当前元素决策后的最大价值
     */
    int forceRecursion(int n, int wight) {
        if (n <= 0) {
            // 递归终止条件
            return 0;
        } else if (w[n - 1] > wight) {
            // 当前物品重量超出背包剩余容量,返回不放该物品的最大价值
            return forceRecursion(n - 1, wight);
        } else {
            // 返回放和不放当前物品所能达到的最大价值的较大值
            return Math.max(forceRecursion(n - 1, wight), v[n - 1] + forceRecursion(n - 1, wight - w[n - 1]));
        }
    }

    /**
     * 动态规划版本
     * 动态规划表示例:
     * w   v   0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16
     * 1   2   0   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2
     * 3   5   0   2   2   5   7   7   7   7   7   7   7   7   7   7   7   7   7
     * 5   9   0   2   2   5   7   9   11  11  14  16  16  16  16  16  16  16  16
     * 7   15  0   2   2   5   7   9   11  15  17  17  20  22  24  26  26  29  31
     */
    int dynamicPrograming() {
        int n = w.length;
        int[][] dp = new int[n][c + 1];
        // 初始化第一列,即剩余容量为零,价值为0
        for (int i = 0; i < n; i++) {
            dp[i][0] = 0;
        }
        // 初始化动态规划表的第一行
        for (int i = 1; i <= c; i++) {
            if (w[0] > i) {
                dp[0][i] = 0;
            } else {
                dp[0][i] = v[0];
            }
        }
        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= c; j++) {
                if (w[i] > j) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
                }
            }
        }
        return dp[n - 1][c];
    }

    /**
     * 动态规划省空间版本
     * 求解动态规划表第i行时,只依赖第i-1行的记录,所以可以压缩动态规划表为1行。
     * 求解单行时,从后往前填充
     */
    int dynamicProgramingPro() {
        int n = w.length;

        int[] dp = new int[c + 1];
        dp[0] = 0;
        // 初始化
        for (int i = 1; i <= c; i++) {
            int value = 0;
            if (i >= w[0]) {
                value = v[0];
            }
            dp[i] = value;
        }
        for (int i = 1; i < n; i++) {
            for (int j = c; j > 0; j--) {
                if (j >= w[i]) {
                    dp[j] = Math.max(dp[j], v[i] + dp[j - w[i]]);
                }
            }
        }
        return dp[c];
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值