动态规划

简介: 动态规划

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

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

一般解决步骤如下:

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最长路、背包问题的应用。分别教会了我变换更新方式、递推和递归、打印路径,这里一定多练习。

相关文章
|
域名解析 缓存 网络协议
DNS服务详解
DNS服务详解
1087 0
|
2天前
|
数据采集 人工智能 安全
|
11天前
|
云安全 监控 安全
|
3天前
|
自然语言处理 API
万相 Wan2.6 全新升级发布!人人都能当导演的时代来了
通义万相2.6全新升级,支持文生图、图生视频、文生视频,打造电影级创作体验。智能分镜、角色扮演、音画同步,让创意一键成片,大众也能轻松制作高质量短视频。
1019 151
|
3天前
|
编解码 人工智能 机器人
通义万相2.6,模型使用指南
智能分镜 | 多镜头叙事 | 支持15秒视频生成 | 高品质声音生成 | 多人稳定对话
|
16天前
|
机器学习/深度学习 人工智能 自然语言处理
Z-Image:冲击体验上限的下一代图像生成模型
通义实验室推出全新文生图模型Z-Image,以6B参数实现“快、稳、轻、准”突破。Turbo版本仅需8步亚秒级生成,支持16GB显存设备,中英双语理解与文字渲染尤为出色,真实感和美学表现媲美国际顶尖模型,被誉为“最值得关注的开源生图模型之一”。
1711 9
|
8天前
|
人工智能 自然语言处理 API
一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸
一句话生成拓扑图!next-ai-draw-io 结合 AI 与 Draw.io,通过自然语言秒出架构图,支持私有部署、免费大模型接口,彻底解放生产力,绘图效率直接爆炸。
654 152
|
10天前
|
人工智能 安全 前端开发
AgentScope Java v1.0 发布,让 Java 开发者轻松构建企业级 Agentic 应用
AgentScope 重磅发布 Java 版本,拥抱企业开发主流技术栈。
620 12