动态规划 经典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]为结尾的>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值