1 动态规划
1.1 基本思想
动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题。
与分治法的区别在于,适用于动态规划算法求解的问题,经分解得到的子问题往往不是互相独立的;若用分治法求解,则分解得到的子问题数目太多,导致最终解决原问题需指数时间。原因在于:虽然子问题的数目常常只有多项式量级,但在用分治法求解时,有些子问题被重复计算了许多次,如果可以保存已解决的子问题的答案,就可以避免大量重复计算,从而得到多项式时间的算法。
动态规划法的基本思路是:构造一张表来记录所有已解决的子问题的答案(无论算法形式如何,其填表格式是相同的)。
1.2 适用条件
适用动态规划的问题必须满足最优化原理和无后效性。
最优化原理(最优子结构性质):
最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质
无后效性:
将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
子问题的重叠性:
动态规划算法的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其他的算法。选择动态规划算法是因为动态规划算法在空间上可以承受,而搜索算法在时间上却无法承受,所以我们舍空间而取时间
2 最大子段和问题描述
给定n个整数(可能为负数)组成的序列a1,a2,…,an。求该序列形如下式的子段和的最大值:
m
a
x
∑
k
=
i
j
a
k
max \sum_{k=i}^{j}a_k
maxk=i∑jak
当所有整数均为负整数时定义其最大子段和为0。
依次定义,所求的最优值为:
m
a
x
{
0
,
m
a
x
1
≤
i
≤
j
≤
n
∑
k
=
i
j
a
k
}
max\{0,{max}_{1\leq i\leq j\leq n}\sum_{k=i}^{j}a_k \}
max{0,max1≤i≤j≤nk=i∑jak}
例如: (a1,a2,a3,a4,a5,a6)=(-2,11,-4,13,-5,-2)
该序列的最大子段和为:
∑
k
=
2
4
a
k
=
20
\sum_{k=2}^4a_k=20
k=2∑4ak=20
3 算法分析
通过对分治算法的分析可知,若记:
b
[
j
]
=
m
a
x
1
≤
i
≤
j
{
∑
k
=
i
j
a
[
k
]
}
,
1
≤
j
≤
n
b[j] = {max}_{1\leq i\leq j}\{\sum_{k=i}^j{a[k]}\},1\leq j \leq n
b[j]=max1≤i≤j{k=i∑ja[k]},1≤j≤n
则所求的最大子段和为:
m
a
x
1
≤
j
≤
n
b
[
j
]
max_{1\leq j\leq n}{b[j]}
max1≤j≤nb[j]
由b[j]的定义可知:
当
b
[
j
−
1
]
>
0
b[j-1]>0
b[j−1]>0时:
b
[
j
]
=
b
[
j
−
1
]
+
a
[
j
]
b[j]=b[j-1]+a[j]
b[j]=b[j−1]+a[j]
否则:
b
[
j
]
=
a
[
j
]
b[j]=a[j]
b[j]=a[j]
由此可得b[j]的动态规划递归式:
b
[
j
]
=
m
a
x
{
b
[
j
−
1
]
+
a
[
j
]
,
a
[
j
]
}
(
1
≤
j
≤
n
)
b[j]=max\{ b[j-1]+a[j], a[j] \} (1≤j≤n)
b[j]=max{b[j−1]+a[j],a[j]}(1≤j≤n)
4 C++实现
int maxSubArray(vector<int>& nums) {
int size=nums.size();
int* b = new int[size];
b[0]=nums.at(0);
for(int i=1;i<size;i++){
if(b[i-1]<0){
b[i]=nums.at(i);
}else{
b[i]=b[i-1]+nums.at(i);
}
}
int maxV=b[0];
for(int i=0;i<size;i++){
if(maxV<b[i]){
maxV=b[i];
}
}
return maxV;
}