动态规划的设计思想与实例(最大子段和、最长公共子序列、0-1背包、编辑距离)

动态规划算法与分治法类似,其基本思想是将总问题分解成若干个子问题,先求解子问题,再结合这些子问题的解得到原问题的解。与分治法不同的是,动态规划求解的问题经分解得到的子问题往往不是相互独立的。

基本思想:

将总问题分解成多个子问题(子问题也可以继续分解,直到无法分解),计算子问题,用一个表保存已解决的子问题的答案,算完子问题后回到总问题时从表中寻找已求得的答案,根据要求挑选最优解,加上总问题的里的具体变化再存入表中

设计步骤:

1.找出最优解的性质,并刻画其结构特征

根据具体问题找出其结构的特点,按该特点分解问题,使问题可以从其子问题的答案中寻找解

2.递归地定义最优值

写递归表达式,即子问题的答案中最合适的应该满足哪些条件 总问题的解 = 最优解(子问题1,子问题2,子问题3……)+ 一定变化

3.以自底向上的方式计算最优值

存储子问题解的表一般为二维数组dp,按从左到右从上到下的顺序从下标(0,0)到(m,n)存储子问题的答案,在计算某一个子问题(r,c)时,根据之前计算好的解来得出答案,例如dp[l][r]=max(dp[r-1][c],dp[r][c-1])+1,表示问题(r,c)的解依据是子问题(r-1,c)和(r,c-1)的解

4.根据计算最优值时得到的信息,构造最优解

看问题的解的具体要求,一般直接为dp[m][n]

实例

最大子段和

给定n个整数(可能为负整数)组成的序列a1,a2……an,求该序列的子段和的最大值(al+al+1+al+2+……+ar-1+ar),当子段和小于0时定义其为0。

分析问题,可以得到特征:

1.子段是连续的

2.当子段中所有负整数与正整数相加的结果小于0时该子段和取0,即子段和大于等于0

3.子段和最大的子段一定会以某个元素ai为结尾

(先简称以ai-1、ai为结尾的子段和最大的两个子段为子段ai-1和子段ai)

结合1和3可以得到关系:子段ai一定包括元素ai(因为以它为结尾),该子段的ai元素的上一位一定是ai(因为子段连续),或者是空(即整个子段只有ai),那么以ai为结尾的子段的最大子段和为:max(以ai-1为结尾的子段的最大字段和+ai,0)

即递归表达式:dp[i]=max(dp[i-1]+a[i],0)(其中dp[i]指子段ai的子段和,a[i]指ai)

(其实不需要a数组,一个dp数组就够了)

#include<iostream>
using namespace std;
int main()
{
   
    int dp[100000],m,n,i;
    while(cin>>n)
    {
   
        for(i=0;i<n;i++)
            cin>>dp[i];
        m=dp[0]=max(dp[0],0);
        for(i=1;i<n;i++)
        {
   
            dp[i]=max(dp[i-1]+dp[i],0);
            m=max(m,dp[i]);
        }
        cout<<m<<'\n';
    }
    return 0;
}

最长公共子序列

给定序列x={x1,x2……xm}和y={y1,y2,……yn},求x和y的最长公共子序列z

已知:子序列z的元素在序列x、y里不一定是连续的,但一定是按序列x、y里的下标的大小从小到大排好的

设dp[i][j]为x、y的从第一个元素开始、长度分别为i、j的子段的最长公共子序列的长度,则对于x[i]、y[j]的关系:

当i=0或jj=0时,x、y其中一边或两边长度为0,公共子序列长度也为0,则:

dp[i][j]=0

当x[i]=y[j]时,x[i]、y[j]直接算为公共子序列里的元素,当前最长公共子序列为上一次的最长公共子序列(dp[i-1][j-1]的时候)加上x[i]、y[j],即:

dp[i][j]=dp[i-1][j-1]+1

当x[i]!&#

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值