算法练习-DP动态规划
今天学习一下动态规划算法的应用与写法,准备机试进行时。
动态规划(英语:Dynamic programming,简称 DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。动态规划往往用于优化递归问题,例如斐波那契数列,如果运用递归的方式来求解会重复计算很多相同的子问题,利用动态规划的思想可以减少计算量。
通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,具有天然剪枝的功能,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用
1.解码方法
1.1题目描述
一条包含字母 A-Z 的消息通过以下方式进行了编码:
‘A’ -> 1
‘B’ -> 2
…
‘Z’ -> 26
给定一个只包含数字的非空字符串,请计算解码方法的总数。
示例 1:
输入: “12”
输出: 2
解释: 它可以解码为 “AB”(1 2)或者 “L”(12)。
示例 2:
输入: “226”
输出: 3
解释: 它可以解码为 “BZ” (2 26), “VF” (22 6), 或者 “BBF” (2 2 6) 。
来源:https://leetcode-cn.com/problems/decode-ways/
1.2思路
这个和青蛙跳台阶差不多思路,都是动态规划问题。如果字符串第一位就是0,出错,那么返回0即可。如果当前位置是0,向前查找上一个位置是不是1或者2,如果不是,编码出错,返回0,如果是,当前的结果数量就是上一次的数量。如果当前位置是1-6之间的数字,我们可以去判断上一个位置是不是1或2,如果是,当前结果数量,就是上一次的加上上次的,因为本次可以有跳一个和跳两个两种。然后更新pre的值。
1.3代码
1 |
|
2.最长回文子串
2.1题目描述
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
来源:https://leetcode-cn.com/problems/longest-palindromic-substring/
2.2思路
第一想法就是直接暴力搜索,分偶数长度和奇数长度回文字符串,直接尝试保存目前最长的回文子串即可。
2.3代码
1 |
|
3.最大子序和
3.1题目描述
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
来源:https://leetcode-cn.com/problems/maximum-subarray/
3.2思路
动态规划的是首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 result
如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留并加上当前遍历数字
如果 sum <= 0,则说明 sum 对结果无增益效果,需要舍弃,则 sum 直接更新为当前遍历数字
每次比较 sum 和 result的大小,将最大值置为result,遍历结束返回结果
时间复杂度:O(n)
3.3代码
1 |
|
4.不同路径
4.1题目描述
一个机器人位于一个 m x n 网格的左上角 。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。
问总共有多少条不同的路径?
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
向右 -> 向右 -> 向下
向右 -> 向下 -> 向右
向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
4.2思路
第一个想到的直接计算阶乘即可,由于只能向右或者向下,那么根据数学知识可以很容易算出来结果,就是da。由于本章练习的是动态规划算法,所以还是用动态规划来写吧。
首先找到递推式,就是dp[i][j] = dp[i-1][j] + dp[i][j-1];
然后如果在最上面一行或者最左面,那么结果就是1。
4.3代码
1 |
|
5.不同路径升级版
5.1题目描述
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
- 向右 -> 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右 -> 向右
5.2思路
和前面的差不多,只不过加了障碍,把有障碍的地方去掉,dp[i][j]=0,然后先把第一行和第一列初始化完毕,不能和上面的一样一起初始化了。
5.3代码
1 |
|
6.最小路径和
6.1题目描述
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
来源:https://leetcode-cn.com/problems/minimum-path-sum/
6.2思路
使用动态规划算法,dp数组记录该点到起始点的最短距离,边界时候单独处理,非边界取上一次计算的最小值加上本点的值即可。思路清晰代码也很简单。dp数组可以使用malloc动态分配即可,偷懒直接申请了一个挺大的空间。时间复杂度为遍历二维数组花费时间。
6.3代码
1 |
|
7.编辑距离
7.1题目描述
给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
示例 1:
输入: word1 = “horse”, word2 = “ros”
输出: 3
解释:
horse -> rorse (将 ‘h’ 替换为 ‘r’)
rorse -> rose (删除 ‘r’)
rose -> ros (删除 ‘e’)
示例 2:
输入: word1 = “intention”, word2 = “execution”
输出: 5
解释:
intention -> inention (删除 ‘t’)
inention -> enention (将 ‘i’ 替换为 ‘e’)
enention -> exention (将 ‘n’ 替换为 ‘x’)
exention -> exection (将 ‘n’ 替换为 ‘c’)
exection -> execution (插入 ‘u’)
来源:https://leetcode-cn.com/problems/edit-distance/
7.2思路
动态规划:
dp[i][j] 代表 word1 到 i 位置转换成 word2 到 j 位置需要最少步数
所以,
当 word1[i] == word2[j],dp[i][j] = dp[i-1][j-1];
当 word1[i] != word2[j],dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
其中,dp[i-1][j-1]到dp[i][j]需要进行替换操作,dp[i-1][j]到d[i][j]需要进行删除操作,dp[i][j-1] 到d[i][j]需要进行添加操作。
7.3代码
1 |
|
8.买卖股票的最佳时机
8.1题目描述
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。
来源:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii
8.2思路分析
每天我们其实是有四个状态,买入当前价格的股票,以当前价格的股票卖出。第二次买入股票,第二次卖出股票。
s0代表初始状态,初始时钱是 0。s1代表第一次买入后当前的钱,s2代表第一次卖出后当前的前,s3代表第二次买入后当前的钱,s4代表第二次卖出后当前的钱。然后我们只需要更新每天的这四个状态即可。
8.3代码
1 |
|
9.三角形最小路径和
9.1题目描述
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:
1 |
|
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
来源:https://leetcode-cn.com/problems/triangle
9.2思路
从倒数第二行开始计算每个数字到下一个节点的最小值,并加上当前节点值存入当前节点,最后triangle[0][0]中存储的就是最终的最短路径结果。
9.3代码
1 |
|
10.最大正方形
10.1题目描述
在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
示例:
输入:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
输出: 4
来源:https://leetcode-cn.com/problems/maximal-square
10.2思路分析
第一想法就是使用动态规划算法,将dp初始化为全零(不初始化直接用会导致测试时出错),如果当前点是1,然后dp[i][j] 取左侧、左上角、上侧最小值加一为当前点dp值,如果当前dp值大于maxqlen就将maxqlen置为当前dp值。最后返回maxqlen*maxqlen即可。中间最开始看错了输汝,以为矩阵里面是整形导致一直判断出错,要细心啊。
10.3代码
1 |
|