动态规划(一)DynamicProgramming

本文深入探讨动态规划,以01背包问题为例,详细解释了从递归到动态规划的转化过程,包括递归、记忆递归型和递推的解题方法。动态规划通过避免重复计算提高效率,适用于具有最优子结构和重叠子问题的问题。此外,文章还提供了背包问题的完整代码,并以斐波那契数列为实例进一步巩固动态规划思维。
摘要由CSDN通过智能技术生成

看这个笔记,比价通俗易懂地介绍了动态规划有道云笔记

经典的01背包问题

有一个包和n个物品,包的承重为v,每个物品都有各自的重量和价值,问当从这n个物品中选择多个物品放在包里而物品总重量不超过包的承重v时,能够得到的最大价值是多少?[对于每个物品不可以取多次,最多只能取一次,之所以叫做01背包,0表示不取,1表示取]

输入:物品的重量序列w,价值序列p,包的承重v

递归

这个问题首先可以用递归来解决:

对于最后一个物品,我们有选或者不选两种决策,1)选:价值为p[n]+m(n-1,v=v-w[n]),即当前物品的价值加上扣除当前物品重量后前n-1个物品的最优解 2)不选:我们将v全部分配给前n-1个物品,求最优解,m(n-1,v).

所以递归式为:

m(n,v) = max(m(n-1,v-w[n])+p[n] , m(n-1,v))

边界为:

n=0时,如果v能装下w[0]则返回p[0]否则返回0

n<0或者v<0,返回0

如果v连当前元素都装不下,直接将v分配给前n-1个物品

根据上面这个递归式,我们就可以写出完整的递归代码:

    /**
     * 
     * @param w
     *          重量表
     * @param p
     *          价值表
     * @param volumn
     *          背包的最大承重
     * @return 不超过最大承重的情况下所能装载物品的最大价值总和
     */
    public static int recursion(int[] w, int[] p, int volumn) {

        return recursion(w, p, w.length - 1, volumn);
    }

    private static int recursion(int[] w, int[] p, int index, int volumn) {
        if (index < 0 || volumn <= 0) {
            return 0;
        }
        if (index == 0 && volumn >= w[0]) {
            return p[0];
        }
        if (index == 0 && volumn < w[0]) {
            return 0;
        }
        if (volumn < w[index]) {
            return recursion(w, p, index - 1, volumn);
        } else {
            return Math.max(recursion(w, p, index - 1, volumn), // 不选
                recursion(w, p, index - 1, volumn - w[index]) + p[index]); // 选
        }
    }
}

但是这份代码在我的电脑上执行,100个元素执行时间为recursion持续时间:46844毫秒

int[] w = Util.getRandomArr(100, 1, 50);
    int[] p = Util.getRandomArr(100, 1, 30);
    int total = 100;
    Instant now = Instant.now();
    System.out.println(recursion(w, p, total));
    System.out.println("recursion持续时间:" + (Instant.now().toEpochMilli() - now.toEpochMilli()) + "毫秒");

效率很低,因为我们重复计算了。
这里写图片描述

我们定义前x项分配y重量的xy为一个状态,这个状态对应着一个子问题,这个子问题在递归过程中将会被多次求解。

我们一共有2^n个子问题(可能重复),就要求解2^n次。

接下来,我们就要考虑如何进行改进,我们自然而然就可以想到如果每算出一个状态的解就保存起来,下次用到其值的时候直接取用,则可免去重复计算。

这里可以想到有x*y即n*v个状态,那么可以用n²的时间复杂度和一个辅助的二维数组来完成,比指数函数的效率要高很多。

记忆递归型

根据这个思路,我们就可以将上面的代码进行改进,使之成为记忆递归型的动态规划程序:

    public static int recursion_m(int[] w, int[] p, int volumn) {

        int[][] state = new int[w.length][volumn + 1];
        return recursion_m(w, p, w.length - 1, volumn, state);
    }

    private static int recursion_m(int[] w, int[] p, int index, int volumn, int[][] state) {
        if (index < 0 || volumn <= 0) {
            return 0;
        }
        if (index == 0 && volumn >= w[0]) {
            return p[
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值