动态规划

简介: 动态规划

动态规划是一种用来解决最优化问题的算法思想,其会记录下子问题的最优解,综合子问题的最优解得到最优解。

上面有两个关键,划分子问题和记录子问题,其中划分子问题有一个重要的状态转移方程

一般解决步骤如下:

1.确定维数,确定dp数组。

2.每一维采用下面表述,为i或者前i。

3.之后问题可以描述为dp[i]为恰好为i(或者前i)的XXX(问题描述),然后思考dp[i-1]得出状态转移方程。


动态规划的题目主要可能得多看习题领悟,这里针对典型例题来领悟动态规划的过程。

最大连续子序列和

问题:给定一个序列(整数或浮点数),求出其中连续的子序列和最大的那一个。

例:序列{-10 1 2 3 4 -5 -23 3 7 -21},其最大的连续子序列为{1 2 3 4}或{3 7},最大和为10。


首先涉及到一个序列,应该是一维问题,应该可以用一个一维数组来记录子问题的最优解,第二步dp[i]表述恰好为i时在最大连续和(前i肯定不合适),第三步思考dp[i-1]和dp[i] 的关系,如果dp[i-1]<0,显然dp[i]=a[i],否则加上前面的dp[i-1]dp[i]肯定更大,于是状态转移方程得出。最后问题思考的序列完找出dp[i]最大的即可。


附代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10001;
int dp[maxn],a[maxn];//dp[i] 表示结尾恰好为i时最大和 
int main(){
  int n;
  while(scanf("%d",&n),n){
    fill(dp,dp+n+1,-1);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    int max=-100000,k=-1;
    for(int i=1;i<=n;i++)
    {
      if(dp[i-1]>0)dp[i]=dp[i-1]+a[i];  //状态转移 
      else dp[i]=a[i];
      if(max<dp[i])        //更新最大值 
      {
        max=dp[i];
        k=i;
      }
    }
    if(max<0)printf("%d %d %d\n",0,a[1],a[n]);
    else {
    printf("%d ",max);
    for(int i=k;max!=0||dp[i-1]==0;i--)
    {
      max-=a[i];
      if(max==0&&dp[i-1]!=0)
      printf("%d ",a[i]);
    }
    printf("%d\n",a[k]);
    }
  } 
return 0; 
}

最长上升子序列

问题:给一个数组a1, a2 … an,找到最长的不下降子序列ab1<ab2<=… <abk,其中b1<b2<…bk,输出长度即可。


思考:维度应该一维,dp[i]应该表示以i结尾的最大不下降子序列长度,前面如果数字都比a[i]大那么dp[i]就为1,否则只要前面dp[j]对应a[j]<a[i],且长度+1后比dp[i]长度大,说明a[i]可以接在a[j]后面得到最大长度。


代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5001;
char a[maxn],dp[maxn];
int main(){
  int n,length=0;
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
  scanf("%d",&a[i]) ;
  fill(dp,dp+n+1,0);
  a[0]=-1;
  for(int i=1;i<=n;i++)
  {
    for(int j=0;j<i;j++) {
      if(a[j]<a[i]&&dp[j]+1>dp[i])
      dp[i]=dp[j]+1;
    }
    if(length<dp[i])length=dp[i];
  }
  printf("%d\n",length);
  return 0;
}

最长公共子序列


问题:给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence)。比如字符串1:BDCABA;字符串2:ABCBDAB

则这两个字符串的最长公共子序列长度为4,最长公共子序列是:BCBA


分析:两个字符串,应该是二维,dp[i][j]应该表示为两个字符串第i,j位之前的最长公共子序列数,如果两个字符串第ij位相等,长度肯定是dp[i-1][j-1]+1,否则应该是dp[i-1][j]或者dp[ii][j-1]中的最大值。


代码如下:

#include<iostream>
#include<algorithm>
#include<string> 
using namespace std;
const int maxn=101;
string a,b;
int dp[maxn][maxn];
int main(){
  while(cin>>a>>b){
  fill(dp[0],dp[0]+maxn*maxn,0);
  int n=a.size(),m=b.size();
  for(int i=1;i<=n;i++){
  for(int j=1;j<=m;j++){
    if(a[i-1]==b[j-1])dp[i][j]=dp[i-1][j-1]+1;
    else {
      if(dp[i][j-1]>dp[i-1][j])
        dp[i][j]=dp[i][j-1];
        else dp[i][j]=dp[i-1][j];
    }
  }
  }
  a.clear();b.clear();
  cout<<dp[n][m]<<endl;}
return 0; 
}

简单取了三个例子,此外还有最长回文子串、dag最长路、背包问题的应用。分别教会了我变换更新方式、递推和递归、打印路径,这里一定多练习。

相关文章
|
6月前
|
算法 测试技术 C++
|
6月前
|
C++ NoSQL 容器
动态规划二
动态规划二
|
算法
【学会动态规划】按摩师(11)
【学会动态规划】按摩师(11)
53 0
|
6月前
|
存储 JavaScript 机器人
动态规划问题
动态规划问题
42 0
|
6月前
动态规划1
动态规划1
38 0
动态规划1
|
存储
【动态规划】
【动态规划】
|
机器学习/深度学习 算法
动态规划详解
前段时间一直在做关于数据结构的题,也算是对数据结构有了一定的了解,知道了有些数据结构的基本算法。现在刚刚开始接触动态规划,其实写这篇文章的初衷是一来锻炼一下自己的总结能力,二来也是希望通过这篇文章,来指引和我一样的初学者,废话不多说了,开始吧。
57 0
动态规划-子序列问题
前言 在上篇文章我们学习了动态规划的公共子序列问题,现在我们来学习下动态规划的单字符串子序列问题。
|
算法 前端开发 JavaScript
理解动态规划
理解动态规划
理解动态规划