动态规划 经典DP

来源:互联网 发布:网络教育规范收费标准 编辑:程序博客网 时间:2024/05/21 09:19
动态规划

动态规划的基本思想

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

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

结果,⾃底向上的求解。 动态规划算法可分解成从先到后的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]为结尾的>
原创粉丝点击