LeetCode基础算法-动态规划

LeetCode基础算法-动态规划

LeetCode 动态规划


动态规划的核心步骤:

  1. 查看大问题的最优解能否使用小问题的最优解lai来解决。
  2. 如果能解决,那么总结出大问题的最优解和小问题最优解之间的关系。
  3. 将关系应用,解决问题。

1. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

分析:

  1. 假设我们到达楼顶需要n步,那么我们到达第n阶楼梯时,我们有那些方法呢?
  2. 我们有2种方法,就是从第n-2阶直接跨2阶到达第n阶;我们也可以从第n-1阶跨1阶到达n阶。
  3. 这就是典型的动态规划,dp[i] = dp[i-1] + dp[i-2].
  4. 我们首先需要求出1~n-1的方法,才能求出到达第n阶的方法。
    public int climbStairs(int n) {
        if (n == 1) return 1;
        int[] dp = new int[n];
        dp[0] = 1;
        dp[1] = 2;
        for (int i = 2; i < n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n - 1];

    }

2. 最大子序和

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

示例:

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

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

解题思路:
下面介绍动态规划的做法,复杂度为 O(n)。
步骤 1:令状态 dp[i] 表示以 A[i] 作为末尾的连续序列的最大和(这里是说 A[i]必须作为连续序列的末尾)。
步骤 2:做如下考虑:因为 dp[i] 要求是必须以 A[i] 结尾的连续序列,那么只有两种情况:
这个最大和的连续序列只有一个元素,即以 A[i] 开始,以 A[i] 结尾。
这个最大和的连续序列有多个元素,即从前面某处 A[p] 开始 (p<i),一直到 A[i] 结尾。

对第一种情况,最大和就是 A[i] 本身。
对第二种情况,最大和是 dp[i-1]+A[i]。

于是得到状态转移方程:
dp[i] = max{A[i], dp[i-1]+A[i]}

这个式子只和 i 与 i 之前的元素有关,且边界为 dp[0] = A[0],由此从小到大枚举 i,即可得到整个 dp 数组。接着输出 dp[0],dp[1],...,dp[n-1] 中的最大子即为最大连续子序列的和。

    public int maxSubArray(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int result = nums[0];
        for (int i = 1; i < nums.length; i++) {
            dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]);
            result = Math.max(result, dp[i]);

        }
        return result; 
    }

3. 打家劫舍

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

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

解题思路:
下面介绍动态规划的做法,复杂度为 O(n)。
步骤 1:令状态 dp[i] 表示以 A[i]作为被劫持房间的最大值。
步骤 2:做如下考虑:因为 dp[i]代表劫持到第i个房间的最打值,那么只有两种情况:
劫持该房间。
不劫持到该房间

对第一种情况,最大值就是 dp[i-2]+A[i] 本身。
对第二种情况,最大和是 dp[i-1]。

于是得到状态转移方程:
dp[i] = max{dp[i-2]+A[i], dp[i-1]}

这个式子只和 i 与 i 之前的元素有关,且边界为 dp[0] = A[0],dp[1]=max(dp[0],dp[1])由此从小到大枚举 i,即可得到整个 dp 数组。

    public int rob(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        if (nums.length == 1) {
            return nums[0];
        }
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = Math.max(dp[0], nums[1]);
        for (int i = 2; i < nums.length; i++) {
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
        }
        return dp[nums.length - 1];
    }


4. 买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。

示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

解题思路:

    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) {
            return 0;
        }
        int[] profits = new int[prices.length];
        profits[0] = 0;
        for (int i = 1; i < profits.length; i++) {
            profits[i] = profits[i] - profits[i - 1];
        }
        int max = 0;
        int temp = 0;
        for (int i = 1; i < profits.length; i++) {
            if (temp + profits[i] > 0) {
                temp += profits[i];
            } else {
                temp = 0;
            }
            max = Math.max(temp, profits[i]);
        }
        return max;
    }

5. 零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:

输入: coins = [2], amount = 3
输出: -1
说明:
你可以认为每种硬币的数量是无限的。

解题思路:

  1. 使用动态规划的思想来解决问题.
  2. 11 = min{1+f(11-1),1+f(11-2),1+f(6)}
  3. 大问题的最优解可以根据小问题的最优解解答。
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        dp[0] = 0;
        // 注意此处赋值的目的是为了后面的判断。
        for (int i = 1; i <= amount; i++) {
            dp[i] = amount + 1;
        }
        for (int i = 1; i <= amount; i++) {
            for (int coin : coins) {
                if (coin <= i) {
                    int v = 1 + dp[i - coin];
                    if (dp[i] > v) {
                        dp[i] = v;
                    }
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }

6. 零钱兑换II

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

注意: 你可以假设

0 <= amount (总金额) <= 5000
1 <= coin (硬币面额) <= 5000
硬币种类不超过500种
结果符合32位符号整数
示例 1:

输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

解题思路:

  1. 动态规划。

    public int change(int amount, int[] coins) {
        if (amount == 0) return 1;
        if (coins == null || coins.length == 0 || amount < 0) return 0;
        int[] dp = new int[amount + 1];
        // 此处的赋值保证了dp[1] dp[2] dp[5] =1;
        dp[0] = 1;
        for (int i = coins.length - 1; i >= 0; i--) {
            for (int j = 0; j <= amount; j++) {
                if (j - coins[i] >= 0) {
                    dp[j] += dp[j - coins[i]];
                } else {
                    dp[j] = dp[j];
                }
            }
        }
        return dp[amount];
    }


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

推荐阅读更多精彩内容

  • 20岁的一天就这么过了,六点多钟就醒过来的我,早知道昨天就回去了,反正财务管理也听不懂,要有自己的判断能力呀,红和...
    哎呀萝卜阅读 240评论 0 0
  • 1. 9点多钟拜完年回来,在里屋坐了一会儿,爸低声跟我说,叫哥回来。我没问爸叫哥回来有什么事,但是总有种不好的感觉...
    猫玲格阅读 888评论 4 3
  • 转瞬即逝的秋,有着我们留不住的美。 想去看山,带着孩子一起,看山里藏着秋天的锦绣繁华,。看结满了柿子的老树...
    心灵烟火阅读 466评论 0 1
  • 你好像又进步多一点了,现在的行动力变强了,该做的事情没有拖一两个月那么久,看,来新家的第一天,你就做了好多事。 凌...
    天野丢阅读 187评论 1 1
  • 加利福尼亚大学的心理学家鲍姆令德提出了教养方式的两个维度:要求和反应性。要求是指父母是否对孩子的行为建立标准,并坚...
    玥辰_dae7阅读 1,423评论 0 0