动态规划
目的
动态规划算法一般用于求解一些最优问题,例如:最长连续子数和、最长递增子序列、最长公共子序列,最长回文子串、01背包...此类问题都会有最优子结构,求解此类问题可以将问题分解为一个个的子问题,直到边界,再从边界逐渐回推求解问题
对于这些问题,我们使用暴力法会有O()或者指数级的时间复杂度,但实际上暴力法会有大量的重复计算,而我们使用动态规划,可以将这些重复的计算记录下来,之后可以直接使用,避免了大量的重复计算,这也被称为记忆化搜索。
原理
我们都知道递归最重要的有两点: 1.递归边界 2.递归式。 而动态规划最重要也是有两个方面: 1.边界 2.状态转移方程。
求解动态规划问题其实也就是寻找状态转移方程,而寻找有效的状态转移方程就是动态规划的难点,可以记住几种常见的动态规划模版,再多多刷题增进理解。
有效的状态转移方程必须具备无后效性无后效性指的是当前状态记录了历史信息,且无法再改变,未来的决策在已有的一个或若干个状态的基础上进行。
示例:最大连续子数和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
我们假设dp[i]表示以a[i]结尾的子序列的最大和,则dp[i]的取值有两种情况
- 以a[i]开始,以a[i]结束,dp[i]=a[i]
- 以a[i-1]为结尾的最长子序列加上a[i],即dp[i] = dp[i - 1] + a[i]。
只会有这两种情况,dp[i]总是会在之前已经求得的历史结果上进行计算,状态转移方程满足无后效性。故可以得到
dp[i] = max(a[i],dp[i-1] + a[i])
接着找出dp[]的最大值即可 写出如下代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int dp[nums.size()];
dp[0] = nums[0];
// 动态规划
for(int i = 1;i < nums.size();i++){
dp[i] = max(nums[i],dp[i - 1] + nums[i]);
}
// 找最大值
int max = dp[0];
for(int i = 1;i < nums.size();i++){
if(dp[i] > max){
max = dp[i];
}
}
return max;
}
};
以上是一个简单的动态规划入门,之后再更新别的动态规划算法