动态规划基础

动态规划

我们从一道leetcode题目入手,题目198抢钱

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。偷窃到的最高金额 = 2 + 9 + 1 = 12 。

先给出答案(递归版本)

class Solution {
    @Test
    public void test() {
        int rob = rob(new int[]{1, 2, 3, 4, 5, 6, 7});
        System.out.println(rob);
    }
    public int rob(int[] nums) {
        return slove(nums.length - 1,nums);
    }
    public int slove(int n,int[] nums) {
        if(n < 0) return 0;
        return Math.max(nums[n] + slove(n-2,nums),slove(n-1,nums));
    }
}

我们开始分析,首先我们假设是输入的是nums[7] = [1, 2, 3, 4, 5, 6, 7],房子从第零间到第六间

对于这种题目,一般倒着往前分析,对于最后一间房子(也就是第六间房子),我们有两种选择(抢不抢这一户的钱):

第一种(抢了这一户的钱):由于相邻的屋子不能抢,因此我们得到的收益是这一户抢到的钱加上到第四间房子为止所有的钱(我们不知道第四间房子的钱有没有抢)

第二种(没有抢这一户的钱):那我们得到的收益只有到第五间房子为止所有的钱

因此,到光顾完第六间房子为止,我们手里的钱应该是上述两种中的最大值。

在计算完slove(-2)后,会去计算slove(-1),这些都是0,因此我们可以计算出slove(0)的值,也就是num[0] ,之后,再去计算slove(1)的值。。。以此类推得到最终的slove(6).

我们仔细看上面的代码,会发现时间复杂度很高,为什么呢? 因为有大量的重复计算!做如下分析:

我们要先计算出slove(4),才能计算出slove(5),在计算slove(2)的时候,我们依次计算了slove(1)和slove(0)以及slove(-1)、slove(-2)。然后我们紧接着计算了slove(3),有没有发现!我们计算slove(3)的同时,我们又会把slove(2)等等又计算一遍,而slove(2)又会把slove(1)等等再计算一遍!这是完全不必要的!

如果我们这么提交,很悲惨的是

因此我们需要把我们每次计算的slove(n)保存起来,当再次要计算这个值的时候,就可以直接使用这个值,而不需要再次套娃了。

因此我们增加一个数组用来保存我们的slove(n)值,因此代码看起来会像这样:

class Solution {
    @Test
    public void test() {
        int rob = rob(new int[]{1, 2, 3, 4, 5, 6, 7});
        System.out.println(rob);
    }
    public int rob(int[] nums) {
        int[] result = new int[nums.length];
        //由于存在nums全0的情况,因此我们需要将值全部赋为-1
        for (int i = 0; i < result.length; i++) {
            result[i] = -1;
        }
        //添加一个记录slove(n)值数组
        return slove(nums.length - 1,nums,result);
    }
    public int slove(int n,int[] nums,int[] result) {
        if(n < 0) return 0;
        //如果不是-1,就表示赋过值了,直接拿来用。
        if(result[n] != -1) return result[n];
        int max = Math.max(nums[n] + slove(n-2,nums,result),slove(n-1,nums,result));
        //当我们计算出某个slove(n)的时候,我们将它的值保存到对于数组下标中。
        result[n] = max;
        return max;
    }
}

再次尝试,完美!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值