前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >漫画:动态规划系列 第六讲

漫画:动态规划系列 第六讲

作者头像
程序员小浩
发布2020-03-31 15:11:37
3300
发布2020-03-31 15:11:37
举报
文章被收录于专栏:小浩算法小浩算法小浩算法

在前两篇中,我们分别学习了 “三角形最小路径和” 以及“矩形最小路径和” 的问题,相信已经掌握了这类题型的解题方式。我们只要明确状态的定义,基本上都可以顺利求解。

在本节中,我们将回归一道简单点的题目,目的是剖析一下状态定义的过程,并且举例说明如果状态定义错误,会对我们带来多大困扰!希望大家不要轻视!

01

第198题:打家劫舍

第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 。

本题主要剖析状态定义的过程!

强烈建议先进行前面5节内容的学习!

以达到最好的学习效果!

02

图解分析

假设有i间房子,我们可能会定义出两种状态:

  • dp[i] : 偷盗 含 第i个房子时,所获取的最大利益
  • dp[i] : 偷盗 至 第i个房子时,所获取的最大利益

如果我们定义为状态一,因为我们没办法知道获取最高金额时,小偷到底偷盗了哪些房屋。所以我们需要找到所有状态中的最大值,才能找到我们的最终答案。即:

max(dp[0],dp[1],.....,dp[len(dp)-1])

如果我们定义为状态二,因为小偷一定会从前偷到最后强调:偷盗至第i个房间,不代表小偷要从第i个房间中获取财物)。所以我们的最终答案很容易确定。即:

dp[i]

现在我们分析这两种状态定义下的状态转移方程:

如果是状态一,偷盗第i个房间时能获取的最高金额,我们相当于要找到偷盗每一间房子时可以获取到的最大金额。比如下图,我们要找到dp[4],也就是偷盗9这间房子时,能获取到的最大金额。

那我们就需要找到与9不相邻的前后两段中能获取到的最大金额。

我们发现题目进入恶性循环,因为我们若要找到与9不相邻的两端中能偷盗的最大金额,根据dp[i]的定义,我们就又需要分析在这两段中盗取每一间房子时所能获取的最大利益!想想都很可怕!所以我们放弃掉这种状态的定义。

如果是状态二,偷盗第i个房子时,所能获取的最大利益。那我们可以想到,由于不可以在相邻的房屋闯入,所以 至i房屋可盗窃的最大值,要么就是至 i-1 房屋可盗窃的最大值,要么就是至 i-2 房屋可盗窃的最大值加上当前房屋的值,二者之间取最大值。即:

dp[i] = max(dp[i-2]+nums[i], dp[i-1])

如果不能理解可以看下图:

(相当于小贼背了个背包,里边装了之前偷来的财物,每到达下一个房间门口,来选择是偷还是不偷。)

03

代码分析

分析完毕,我们根据第二种状态定义进行求解:

func rob(nums []int) int {
    if len(nums) < 1 {
        return 0
    }
    if len(nums) == 1 {
        return nums[0]
    }
    if len(nums) == 2 {
        return max(nums[0],nums[1])
    }
    dp := make([]int, len(nums))
    dp[0] = nums[0]
    dp[1] = max(nums[0],nums[1])
    for i := 2; i < len(nums); i++ {
        dp[i] = max(dp[i-2]+nums[i],dp[i-1])
    }
    return dp[len(dp)-1]
}

func max(a,b int) int {
    if a > b {
        return a
    }
    return b
}

同样,运行上面的代码,我们发现使用的内存过大。有没有什么办法可以压缩内存呢?我们很容易想到,在小贼偷盗的过程中,不可能转回头去到自己已经偷过的房间!(太蠢)小偷只需要每次将财物搬到下一个房间就行!

根据上面思路,优化后的代码如下:

func rob(nums []int) int {
    if len(nums) < 1 {
        return 0
    }
    if len(nums) == 1 {
        return nums[0]
    }
    if len(nums) == 2 {
        return max(nums[0],nums[1])
    }
    nums[1] = max(nums[0],nums[1])
    for i := 2; i < len(nums); i++ {
        nums[i] = max(nums[i-2]+nums[i],nums[i-1])
    }
    return nums[len(nums)-1]
}

func max(a,b int) int {
    if a > b {
        return a
    }
    return b
}

注:本系列所有教程中都不会用到复杂的语言特性,大家不需要担心没有学过go。算法思想最重要,使用go纯属本人爱好。同时,本系列所有代码均在leetcode上进行过测试运行,保证其严谨性!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小浩算法 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档