算法-理解动态规划

算法-动态规划(1)最大子序和问题
算法-动态规划(2)最长公共子串

深刻理解动态规划的思路, 动态规划里面的三个规则

  • 确定状态
  • 确定边值
  • 确定状态转换方程

思考动态规划的思维方式

  • 第一步: 需要理解dp,
    dp是一维数组还是二维数组还是只是一个变量, 数组的话下标表示什么
  • 第二步 : 确定状态
    确定状态需要两个意识
    1. 最后一步
    2. 子问题
  • 第三步: 确定状态转移方程
  • 第四步: 确定边值

练习一:

爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。

分析

第一步: 需要理解dp,

dp是一维数组还是二维数组还是只是一个变量, 数组的话下标表示什么
这里的变量是 给定n, n 阶你才能到达楼顶, n个台阶, 一个可变变量, 定义一维数组.
dp[n] 对应多少种方法 , 也就是就是最优解

第二步 : 确定状态

确定状态需要两个意识
1. 最后一步
2. 子问题

最后一步, 如果我当前是dp[n], 那么我这个dp[n]是怎么来的. 也就是通过上一步dp[n-1] + 1 或者是dp[n-2] + 2 .

简单粗暴理解, 我不需要关心dp[n-2] 和dp[n-1]. 我只知道, 我要是需要达到
dp[n], 那么我无非就是dp[n-1]再加一步, 和dp[n-2]再加两步. 那就是所有的情况都考虑到了.这个时候就确定了状态转移方程.

也就是从最后一步, 推出了子问题, 从而退出了转移方程

第三步: 确定状态转移方程

dp[n] = dp[n-2] + dp[n-1]

举个简单例子简单校验, 比如n = 3, 总共有3个台阶, 那么
dp[3] = dp[1] + dp[2]

d[1] = 1, 1个台阶, 只走1步
d[2] = 2, 2个台阶, 走1,1或者走2
dp[3] = dp[1] + dp[2] = 3, 走1,1,1 或者 1, 2 或者 2,1

第四步: 确定边值

当n = 0的时候, 直接返回0,
当n = 1的时候, 直接返回1,

代码:

   static func climbStairs(_ n: Int) -> Int {
       if (n == 0) {
           return 0;
       }
       
       if (n == 1) {
           return 1;
       }
       // 确定状态, dp[n] 就是最优解 , 对于问题, 有多少个变量就开多少个数组
       var dp : [Int] = [Int](repeating: 0, count: n)
       // 确定边值
       dp[0] = 1
       dp[1] = 2
       var i = 2;
       while(i < n) {
          // 状态转移方程
           dp[i] = dp[i-1]+dp[i-2]
           i += 1;
       }
       return dp[n - 1]
    }






练习二:

打家劫舍

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

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

分析

第一步: 需要理解dp,

dp是一维数组还是二维数组还是只是一个变量, 数组的话下标表示什么
这里的变量是 给定i, i是第i间房间, 因为可能这一条街总共有total个房间, 小偷从0到i, 到第i间的时候, 是对应最高金额的房间

第二步 : 确定状态

确定状态需要两个意识
1. 最后一步
2. 子问题

最后一步, 如果我当前是dp[i], 那么我这个dp[i]是怎么来的. 也就是通过上一步dp[i-1] 或者是dp[i-2]. 这里跟爬楼梯思路一直, 因为我可能偷, 也可能不偷, 不偷的情况是我上一间已经偷过了.

简单粗暴理解, 我不需要关心dp[i-2] 和dp[i-1]. 我只知道, 我要是需要达到
dp[i], 那么我无非就是dp[i-2]再加当前房间的金额或者就是dp[i-1], 因为我只要能偷, 就能在当前金额上叠加金额,达到最大.

也就是从最后一步, 推出了子问题, 从而退出了转移方程

第三步: 确定状态转移方程

dp[i] = max(dp[i-2] + 当前金额 , dp[i-1] )

第四步: 确定边值

例如 : 输入:[1,2,3,1] 假设数组nums = [1,2,3,1]

d[0] = nums[0]
d[1] = max( nums[1], dp[0])

代码:

    static func rob(_ nums: [Int]) -> Int {
        if (nums.count == 0) {
            return 0
        }
        if (nums.count == 1) {
            return nums[0]
        }
        
        let len = nums.count;

        var dp: [Int] = [Int](repeating: 0, count: len);
        var i = 2;
        var maxResult = 0;
        
        // 边界值处理
        dp[0] = nums[0];
        dp[1] = max( dp[0], nums[1])
        maxResult = max( dp[0], dp[1] )
        
        while (i < len ) {
            dp[i] = max( dp[i-1] ,  dp[i-2] + nums[i])
            maxResult = max(maxResult, dp[i])
            i += 1
        }
        
        return maxResult
    }





练习三:

最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例: 
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

分析

第一步: 需要理解dp

这里求的是最大和, 也就是最优解, 那么就是dp就是最大和, 最优解. 因为需要找到连续的数组, 开始和结束的位置不一定,所以dp记录一个常数, 作为一个变量.

第二步 : 确定状态

确定状态需要两个意识
1. 最后一步
2. 子问题

最后一步, 假设当前的dp是最优解. 对应上一步就是 dp = dp + nums[i], 我不关心上一步的dp怎么来的, 我只关心当前的dp这个是最优解.

这里面要关心的条件是 dp + nums[i] 比 nums[i] 本身要大就可以. 因为如果我 dp + nums[i] 发现比 nums[i] 还要小, 那么就应该直接取dp = nums[i] , 因为, 如果本身dp加上下一个都没有下一个自身大, 就没有必要是dp + nums[i], 直接去nums[i]就可以了

所以取max(nums[i], dp+nums[i]), 所以推导出状态转移方程

第三步: 确定状态转移方程

dp=max(nums[i], dp+nums[i])

第四步: 确定边值

确定nums的边值状态. 如果i= 0, 那么dp = nums[0];

代码

    static func maxSubArray(_ nums: [Int]) -> Int {
        if (nums.count == 0) {
            return 0
        }
        var dp = nums[0]
        var i = 1
        let len = nums.count
        var maxResult = dp
        while (i < len) {
            dp = max(nums[i], dp + nums[i])
            maxResult = max(dp, maxResult)
            i += 1
        }
        
        return maxResult;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容