概述
动态规划
动态规划的基本思想
将⼀个问题分解为⼦问题递归求解,且将中间结果保存以避免重复计算。通常⽤来求最优
解,且最优解的局部也是最优的。求解过程产⽣多个决策序列,下⼀步总是依赖上⼀步的
结果,⾃底向上的求解。 动态规划算法可分解成从先到后的4个步骤:
1. 描述⼀个最优解的结构,寻找⼦问题,对问题进⾏划分。
2. 定义状态。往往将和⼦问题相关的各个变量的⼀组取值定义为⼀个状态。某个状态
的值就是这个⼦问题的解(若有k个变量,⼀般⽤K维的数组存储各个状态下的解,
并可根据这个数组记录打印求解过程)。
3. 找出状态转移⽅程。⼀般是从⼀个状态到另⼀个状态时变量值改变。
4. 以“⾃底向上”的⽅式计算最优解的值。
5. 从已计算的信息中构建出最优解的路径。(最优解是问题达到最优值的⼀组解)
其中步骤1~4是动态规划求解问题的基础,如果题⽬只要求最优解的值,则步骤5可以省
略。
最⼤连续⼦段和
N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续⼦段和
的最⼤值。当所给的整数均为负数时和为0。
1.定义状态:
sum[i]表⽰以第i位为结尾的最⼤⼦段和,ans为整个序列的最⼤字段和. 2.状态转移:
sum[i] = max{sum[i-1]+a[i],a[i]}
ans = max{sum[1],sum[2].....sum[n]}
sum[i]只与sum[i-1]有关,我们可以不⽤数组来记录,只⽤sum记录以当前元素结尾的最⼤⼦
段和,每次更新sum即可. 3.⾃底向上求解:
ans= sum = 0;
for (i = 1;i <= n;i++){
sum = max(sum,0)+a[i];
ans = max(sum,ans);
}
(模板,套用就行,大数据用long long)
最⻓递增⼦序列
⼦串&⼦序列:
⼦串:指给定字符串中选取的某⼀连续的段
⼦序列:可以不连续,但是要保证原字符串的顺序
⽅法⼀(时间复杂度O(n^2))
1.定义状态:
dp[i]表⽰以第i位为结尾的最⻓递增⼦序列. 2.状态转移:
我们可以在所有以a[j](a[j]<a[i])为结尾的⼦序列末尾加上a[i],构成以a[i]为结尾的
最⻓递增⼦序列,⻓度为以a[j]为结尾的最⻓递增⼦序列的⻓度+1. dp[i]=max{dp[j]+1}(a[j]<a[i])
3.⾃底向上求解
for (int i = 1;i <= n;i++) dp[i] = 1;
for (int i = 1;i <= n;i++){
int j = i-1;
while (j > 0){
if (a[i] > a[j]){
dp[i] = max(dp[i],dp[j]+1);
}
j--;
}
}
⽅法⼆(时间复杂度O(nlogn))
1.定义状态:
f[j]表⽰⻓度为j的⼦序列的最后⼀项的最⼩值(规定当不存在⻓度为j的递增⼦序列时⻓度
2
f[j]表⽰⻓度为j的⼦序列的最后⼀项的最⼩值(规定当不存在⻓度为j的递增⼦序列时⻓度
f[j]=INF)
2.状态转移:
在处理到到第i位时, 如果a[i] > f[j],那么a[i]可以做⻓度为j+1位的组序列的的最后⼀项,
f[j+1]=min{f[j+1],a[i]}
f[1]<f[2]<f[3]...<f[j]<a[i]<f[i+1]
j = max{j|f[j]>a[i]}(⼆分查找)
f[j+1] = a[i]
ans = max(ans,j+1)
⼆分的时间复杂度为O(logn),总的时间复杂度O(nlogn)
3.⾃底向上求解:
(略)
最⻓公共⼦序列问题(LCS)
1.定义状态:dp[i][j]表⽰a串的前i位,和b串的前j位的最⻓公共⼦序列⻓度。
2.状态转移:
(1)当a[i] == b[j]时,dp[i][j] = dp[i-1][j-1] + 1
(2)当a[i] != b[j]时,dp[i][j] = max{dp[i-1][j],dp[i][j-1]}
3.⾃底向上求解:
for (int i = 0;i <= lena;i++) dp[i][0] = 0;
for (int i = 0;i <= lenb;i++)dp[0][i] = 0;
for (int i= 1;i <= lena;i++){
for (int j = 1;j <= lenb;j++){
if (a[i] == b[j]){
dpi][j] = dp[i-1][j-1] + 1;
}
else{
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
或者写成
for (int i = 0;i <= lena;i++) dp[i][0] = 0;
for (int i = 0;i <= lenb;i++) dp[0][i] = 0;
for (int i= 1;i <= lena;i++){
for (int j = 1;j <= lenb;j++){
dp[i][j]=Max(dp[i-1][j],dp[i][j-1],dp[i-1][j-1]+(a[i]==b[j]?1:0));
}
}
最⻓公共⼦串
1.定义状态:dp[i][j]表⽰以a串第i位和b串的第j位结尾的最⻓公共⼦串⻓度。
2.状态转移:
(1)当a[i] == b[j]时,dp[i][j] = dp[i-1][j-1] + 1
(2)当a[i] != b[j]时,dp[i][j] = 0
3.⾃底向上求解:
for (int i = 0;i <= lena;i++) dp[i][0] = 0;
for (int i = 0;i <= lenb;i++) dp[0][i] = 0;
for(int i= 1;i <= lena;i++){
for (int j = 1;j <= lenb;j++){
if (a[i] == b[j]){
dp[i][j] = dp + 1;
}
else{
dp[i][j] = 0;
}
}
}
<a[i])为结尾的⼦序列末尾加上a[i],构成以a[i]为结尾的 最⻓递增⼦序列,⻓度为以a[j]为结尾的最⻓递增⼦序列的⻓度+1.="" dp[i]="max{dp[j]+1}(a[j]<f[2]<f[3]...<f[j]<a[i]</f[2]<f[3]...<f[j]<a[i]</a[i])为结尾的⼦序列末尾加上a[i],构成以a[i]为结尾的>
最后
以上就是乐观小蝴蝶为你收集整理的动态规划 经典DP的全部内容,希望文章能够帮你解决动态规划 经典DP所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复