动态规划经典题目之最大子序和
最近在学习动态规划算法,通过一道经典例题:最大子序和(力扣-53题)来分享一下动态规划的算法思想。
一、什么是动态规划
动态规划(Dynamic Programming)是一种大事化小、小事化无的解决问题方式。在将大规模问题化解为小规模问题的过程中,保存对每个小规模问题的处理结果,这样在后面处理大规模问题时,可以直接使用这些小规模问题的结果。
二、动态规划的特点
动态规划具备下列三个特点
- 把原来的问题分解成几个相似的子问题;
- 所有的子问题都只需要解决一次;
- 存储子问题的解。
三、动态规划的解决方法
首先我们考虑写出一个子问题状态的定义设置为f(n),对第一个子问题求解,就是f(1),对第二个子问题求解就是f(2)···这里对状态定义的要求就是:一定要形成递推关系。
接下来利用前i - 1个f(n),通过一个式子求出第i个状态的结果,即f(i) = f(f(1),f(2),···f(i - 1)),这个式子就是状态转移方程,我们在思考如何用f(1)…f(n -1)求出f(n)的过程实际就是在列出状态方程的过程。
下面利用题目进行实战尝试,题目描述及示例如下:
假设整数数组nums的长度是n,那么其下标为0~n-1。首先定义出状态,f(i)代表nums数组以第i个数字作为结尾时连续子数组的最大和,那我们的结果肯定是:
m
a
x
(
f
(
0
)
.
.
.
f
(
n
−
1
)
)
max(f(0)...f(n - 1))
max(f(0)...f(n−1))
所以我们只需要求出每个位置的f(i),然后返回其中最大的那个就是我们所需要的结果。
那么如何求出f(i)呢?
我们可以将nums[i]这个数字与它和前面第(i - 1)个解之和进行比较,取两者之间更大的那个,因此状态转移方程应该是:
f
(
i
)
=
m
a
x
(
f
(
i
−
1
)
+
n
u
m
s
[
i
]
,
n
u
m
s
[
i
]
)
f(i) = max(f(i - 1) + nums[i], nums[i])
f(i)=max(f(i−1)+nums[i],nums[i])
这里也很好理解,如果f(i - 1) + nums[i]的和更大,那么nums[i]就加入这个子序列,变成目前最大的子序列,如果两者之和还没有nums[i]自己大,那就另起炉灶,将nums[i]这个数字作为数组以i为结尾连续子数组的最大和。
代码如下:
class Sloution{
public:
int maxSubArray(vector<int>& nums){
int n = nums.size();
vector<int>dp(n,0);//构造一个与nums数组一样长的dp数组,用来存放子数组的最大和
dp[0] = nums[0];//以第一个数字为结尾的最大子序列和只能是它本身,这是状态的初始化部分
int ret = nums[0];//ret用来存放dp数组中最大的值
for(int i = 1; i < n; i++){
dp[i] = max(dp[i - 1] + nums[i], nums[i]);//当前以i为结尾的连续最大子序列的和
if(ret < dp[i]){
ret = dp[i];//也可不设置ret变量,最后将dp数组遍历一遍找到最大值,但是会多进行一次规模为N的循环。
}
}
return ret;
}
};
总结:
动态规划问题需要考虑的重点是:
- 如何定义状态,即f(i);
- 如何写出状态间的状态转移方程,即如何用f(1)···f(i - 1)表示出f(n);
- 状态的初始化,即注意边界部分;
- 勤加练习,多多总结。