我是靠谱客的博主 乐观小蝴蝶,最近开发中收集的这篇文章主要介绍动态规划 经典DP,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

 

 

动态规划

动态规划的基本思想

将⼀个问题分解为⼦问题递归求解,且将中间结果保存以避免重复计算。通常⽤来求最优

解,且最优解的局部也是最优的。求解过程产⽣多个决策序列,下⼀步总是依赖上⼀步的

结果,⾃底向上的求解。 动态规划算法可分解成从先到后的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所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部