线性dp 总结详解

news2024/9/21 11:25:27

就是感觉之前 dp 的 blog 太乱了整理一下。

LIS(最长上升子序列)

例题

给定一个整数序列,找到它的所有严格递增子序列中最长的序列,输出其长度。

思路

拿到题目,大家第一时间想到的应该是\Theta (n^2)的暴力(dp)做法:

#include <bits/stdc++.h>
using namespace std;
int a[maxn],dp[maxn];
int main(){
	int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        dp[i]=1;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++){
            if(a[j]<a[i])
                dp[i]=max(dp[i],dp[j]+1);
        }
    }
    int ans=-1;
    for(int i=1;i<=n;i++) 
        ans=max(ans,dp[i]);
    cout<<ans<<endl;
    return 0;
}

其中,dp_i表示以a_i为结尾的最长上升子序列的长度。而这,也就是 dp 的做法。当然,我们可以用用贪心+二分优化这个算法。这里就不详细展开说了,但是给出代码:

#include <bits/stdc++.h>
using namespace std;
int mn[maxn],a[maxn];
int binary_search(int a[],int r,int x){//二分查找,返回a数组中第一个>=x的位置
    int left=1;
    while(left<=right){
        int mid=(left+right)>>1;
        if(a[mid]<=x)
            left=mid+1;
        else 
            right=mid-1;
    }
    return left;
}
int main(){
	int n;
    cin>>n;
    for(int i=1;i<=n;i++) {
        cin>>a[i];
        mn[i]=INF;//由于mn中存的是最小值,所以mn初始化为INF 
    }
    mn[1]=a[1]; 
    int ans=1;//初始时LIS长度为1 
    for(int i=2;i<=n;i++){
        if(a[i]>mn[ans])//若a[i]>=mn[ans],直接把a[i]接到后面 
            mn[++ans]=a[i];
        else//否则,找到mn中第一个>=a[i]的位置mn[j],用a[i]更新mn[j] 
            mn[binary_search(mn,ans,a[i])]=a[i];
    }
    cout<<ans<<endl; 
    return 0;
}

时间复杂度:\Theta(n\: log\: n)

LCS(最长公共子序列)

例题:P1439

思路

考虑暴力:设两个字符串的长度为n和m,对于两个字符串的每个字符,我们可以选,也可以不选。得到两个子序列后,比较是否相同要max(n,m),所以时间复杂度\Theta(2^{n+m}+max(n,m))

所以还是老老实实 dp 吧。

dp_{i,j}表示字符串 s 的第 i 位和字符串 t 的第 j 位的LCS长度。

那么有两种情况:

  • s_i=t_j:那么字符串 s 和字符串 t 的LCS长度就增加 1。
  • s_i\neq t_j:那么字符串 s 和字符串 t 的LCS长度无法继续增加,等于max(dp_{i-1,j},dp_{i,j-1})

Code

#include <bits/stdc++.h>
using namespace std;
int dp[maxn][maxm];//dp[i][j]表示s的第i位和t的第j位的LCS 
int main(){
	string s,t;
	cin>>s>>t;
	for(int i=0;i<=s.size();i++) {
		dp[0][i]=0;
		dp[i][0]=0;
	}//初始化 
	for(int i=1;i<=s.size();i++){
		for(int j=1;j<=t.size();j++){
			if(s[i-1]==t[j-1])
				dp[i][j]=dp[i-1][j-1]+1;//相等就加1 
			else
				dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
		}//不相等就比较 
	}
	cout<<dp[s.size()][t.size()]<<endl;
	return 0;
}

LCIS(最长公共上升子序列)

例题:CF10D

思路

看到题目,第一反应就是先求 LCS,再求 LIS。

对于(a_i,b_j)状态时 ,由于 dp 状态就决定了,b_j是一定作为这个状态下 LCIS 的结尾的,所以对于a_i,就有两种情况,将其纳入LCIS或者不纳入。

  • a_i\neq b_j -> dp_{i,j}=dp_{i-1,j}
  • a_i=b_j -> dp_{i,j}=dp_{i-1,j-1}

然后用滚动数组优化一下就OK力。

Code

#include <bits/stdc++.h>
using namespace std;
int dp[maxn],a[maxn],b[maxn];
int main(){
	int n;
    cin>>n;
    for(int i=1;i<=n;i++)
		cin>>a[i];
    for(int i=1;i<=n;i++)
		cin>>b[i];
	int ans=0;
    for(int i=1;i<=n;i++){
        int tmp=0;
        for(int j=1;j<=n;j++){
            if(a[i]==b[j])
				dp[j]=max(dp[j],tmp+1);
            if(a[i]>b[j])
				tmp=max(tmp,dp[j]);        
            ans=max(dp[j],ans);
        }
    }
    cout<<ans<<endl;
    return 0;
}

字符串编辑距离

例题:P2758

思路

首先,如果 1 个字符串的长度为 0,那答案就是另一个字符串的长度。所以,我们令dp_{i,j}表示

a_{1...i}转换为b_{1...j}所需的最少操作次数。

也就是说:dp_{0,j}=jdp_{i,0}=i

接下来,继续思考,发现如果a_i=b_j,那么相同,无需变化dp_{i,j}=dp_{i-1,j-1}

如果不相同,dp_{i,j}=min(dp_{i-1,j-1},min(dp_{i-1,j},dp_{i,j-1}))+1

Code

#include <bits/stdc++.h>
using namespace std;
char a[maxn],b[maxn];
int dp[maxn][maxn];
int main(){
	int n;
	cin>>n;
    for(int i=1;i<=n;i++)
    	cin>>a[i];
    for(int i=1;i<=n;i++)
    	cin>>b[i];
    //极端情况
    for(int i=1;i<=strlen(a);i++)
        dp[i][0]=i;
    for(int j=1;j<=strlen(b);j++)
        dp[0][j]=j;
    for(int i=1;i<=strlen(a);i++){
        for(int j=1;j<=strlen(b);j++){
            if(a[i-1]==b[j-1])//相同时距离不变
                dp[i][j]=dp[i-1][j-1];
            else//不同时取三个位置的最小值再+1
                dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
        }
    }
    cout<<dp[strlen(a)][strlen(b)]<<endl;
    return 0;
}

最大子序列和

例题

对于给定序列,寻找它的连续的最大和子数组。

思路

非常简单,你只需要比较一下a_idp_{i-1}+a_i的大小就可以了。

Code

#include <bits/stdc++.h>
using namespace std;
int a[maxn],dp[maxn];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	int ans=-inf;
	for(int i=2;i<=n;i++){
        dp[i]=max(dp[i-1]+a[i],a[i]);
        ans=max(ans,dp[i]);
    }
    cout<<ans<<endl;
    return 0;
}

数字三角形

例题

给出一个数字三角形,现在要从左上角走到第 i 行第 j 列,每一步只能走到相邻的结点,求经过的结点的最大数字和。

思路

非常简单,易得状态转移方程:dp_{i,j}=a_{i,j}+max(dp_{i-1,j},dp_{i-1,j-1})

Code

#include<bits/stdc++.h>
using namespace std;
int a[maxn][maxn],dp[maxn][maxn];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i;j++){
			cin>>a[i][j];
			dp[i][j]=a[i][j];
		}
	}
	for(int i=n-1;i>=1;i--){
		for(int j=1;j<=i;j++)
			dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]);
	}
	cout<<dp[1][1]<<endl;
	return 0;
}

最大子矩阵和

例题

给定一个n行m列的整数矩阵,现在要求一个子矩阵,使其各元素之和为最大。输出这个和。

思路

首先,我们知道这个矩阵最后肯定在第i行和第j行之间。所以,我们用一个数组记录这第 i 行到第 j 行的每一列的和(这句话可能有点绕,可以多读几遍) 。然后,第 i 行到第 j 行的最大子矩阵和就是这个数组的最大子段和。

Code

#include <bits/stdc++.h>
using namespace std;
int a[maxn][maxn];
int sum[maxn];//表示i-j行对应列元素的和
int main(){
	int n;
	cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++)
            cin>>a[i][j];
    }
    int ans=-INF;
    for(int i=0;i<n;i++){
        memset(sum,0,sizeof(sum));
        for(int j=i;j<n;j++){//下面是针对数组sum求最大子段和的动态规划算法
            int tmp=0;
            for(int k=0;k<n;k++){
                sum[k]+=a[j][k];
                tmp+=sum[k];
                if(tmp<0)
					tmp=sum[k];
                ans=max(tmp,ans);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

结尾

这篇 blog 是 up 在 2024CSP-J 初赛前一天晚上赶出来的,主要还是因为 2023CSP-J 初赛考了太多线性 dp(虽然今年大概率不考了)。祝大家 2024CSP-J RP++!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2152197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Windows系统以tomcat为案例,讲解如何新增自启动服务,定时重启服务。

文章目录 引言I 设置服务自启动的常规操作II 安装多个tomcat服务,并设置自启动。III 定时重启服务引言 为了同一个版本安装多个tomcat服务,并设置自启动。使用Windows的任务计划程序来创建一个定时任务,用于重启Tomcat服务。I 设置服务自启动的常规操作 运行窗口输入control…

2024双11有哪些值得入手的好物?2024年双十一好物推荐

随着2024年双十一购物狂欢节的临近&#xff0c;消费者们正摩拳擦掌&#xff0c;准备迎接这场年度最大的网购盛会。面对琳琅满目的促销信息和令人眼花缭乱的商品&#xff0c;如何在海量商品中精准锁定那些真正值得购买的好物&#xff0c;成为每位精明买家的首要任务。本文旨在为…

牛啊,GitHub 代理加速图文教程

大家好&#xff0c;众所周知&#xff0c;GitHub 在国内访问速度堪忧&#xff0c;经常出现访问不了的情况&#xff0c;如果我们去 clone 代码&#xff0c;网速非常差。今天教大家如何给 GitHub 进行加速。 要用到我开发的开源项目 Cloudflare Workers Proxy&#xff0c;它是一个…

视频压缩篇:适用于 Windows 的 10 款最佳视频压缩器

视频压缩器现在对许多想要减小视频大小的视频编辑者来说非常有名。但是&#xff0c;并非所有可以在网上找到的视频压缩器都能产生最佳输出。因此&#xff0c;我们搜索了可以无损压缩视频的最出色的视频压缩器应用程序。本文列出了您可以在离线、在线和手机上使用的十大最佳视频…

2024华为杯研赛D题保姆级教程思路分析+教程

2024年中国研究生数学建模竞赛D题保姆级教程思路分析 D题&#xff1a;大数据驱动的地理综合问题&#xff08;数学分析&#xff0c;统计学&#xff09; 关键词&#xff1a;地理、气候、统计&#xff08;细致到此题&#xff1a;统计指标、统计模型、统计结果解释&#xff09; …

无线领夹麦克风哪个降噪好?一文搞懂麦克风什么牌子的音质效果好

对于视频拍摄、直播来说&#xff0c;一款好的拾音麦克风是不可或缺的。作为一位数码博主&#xff0c;也是会经常拍摄视频讲解&#xff0c;早期没有使用麦克风时&#xff0c;声音不够清晰&#xff0c;而且周围环境音也会同时被收录&#xff0c;导致整个音频的音质效果极差&#…

【多线程】CAS的原理及应用,看这篇文章就够啦

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;多线程 / javaEE初阶 一、CAS概述 CAS&#xff08;Compare and Swap&#xff09;&#xff0c;中文译为 “比较并交换” &#xff0c;是一种无锁算法中常用的原子操作。CAS通常用于实现线程之间的同…

力扣之1459.矩形面积

1. 1459.矩形面积 1.1 题干 表: Points ---------------------- | Column Name | Type | ---------------------- | id | int | | x_value | int | | y_value | int | ---------------------- id 是该表中具有唯一值的列。 每个点都用二维坐标 (x_value, y_value) 表示。 编…

【力扣每日一题——2374. 边积分最高的节点】python

2374. 边积分最高的节点 给你一个有向图&#xff0c;图中有 n 个节点&#xff0c;节点编号从 0 到 n - 1 &#xff0c;其中每个节点都 恰有一条 出边。 图由一个下标从 0 开始、长度为 n 的整数数组 edges 表示&#xff0c;其中 edges[i] 表示存在一条从节点 i 到节点 edges[…

大模型训练实战经验总结

在当今AI技术飞速发展的背景下&#xff0c;定制化大模型的自主训练已成为满足特定行业需求、保障数据安全、提升模型应用效能的关键途径。本文将深度剖析这一过程的核心价值与实践智慧&#xff0c;从数据隐私保护、模型透明度增强&#xff0c;到数据预处理的精细操作&#xff0…

记录一次fs配置导致串线的问题

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 fs在实际的使用过程中也会经常碰到莫名其妙的问题&#xff0c;大部分都是配置问题。 环境 CentOS 7.9 freeswitch 1.10.7 docker 26.1.1 问题描述 组网方案如下。其中的fs-reg是注册服务器&#xff0c;fs1和fs2是…

NEES(Normalized Estimation Error Squared 归一化估计误差平方)

目录 NEES的计算步骤 解释 示例 代码 与RMSE的区别 NEES(Normalized Estimation Error Squared)是一种用于评估状态估计精度的指标,通常用于比较估计值与真实值之间的差异。计算NEES的步骤如下: NEES的计算步骤 获取状态估计: 设定目标的真实状态为。设定状态估计为…

Ubuntu 20.04安装pycharm2022及配置快捷方式

一、下载与安装 1. 下载 在 官网 下载所需版本&#xff0c;如&#xff1a;下载 2022.3.3 - Linux (tar.gz) 2. 安装 设置自定义安装路径(推荐在 /opt/ 路径下)并安装 mkdir -p ~/Documents/software/pycharm/ cd ~/Documents/software/pycharm/ mv ~/Downloads/pycharm-c…

使用 Puppeteer-Cluster 和代理进行高效网络抓取: 完全指南

文章目录 一、介绍&#xff1f;二、什么是 Puppeteer-Cluster&#xff1f;三、为什么代理在网络抓取中很重要&#xff1f;四、 为什么使用带代理的 Puppeteer-Cluster&#xff1f;五、分步指南&#xff1a; 带代理的 Puppeteer 群集5.1. 步骤 1&#xff1a;安装所需程序库5.2. …

光耦知识分享 | 可控硅光耦的行业应用及封装形式

可控硅光耦&#xff08;SCR Optocoupler&#xff09;是一种特殊类型的光耦&#xff0c;通常由红外发光二极管&#xff08;LED&#xff09;和双向可控硅&#xff08;SCR&#xff09;组成&#xff0c;用于实现输入和输出之间的电气隔离和信号传输。能够以最少的外部元器件数控制大…

Postman如何测试WebSocket接口!

01、WebSocket 简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议。 WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直…

网通产品硬件设计工程师:百兆商业级网络隔离变压器有哪些选择呢?

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;今天分享的是网通设备有关工程师产品设计时可供选择的几款百兆商业级网络隔离变压器... 下面我们就一起来看看网通设备有关工程师产品设计时可供选择的几款百兆商业级网络隔离变压器&#xff0c;让您的产品创新在成…

鸿蒙OpenHarmony【轻量系统内核通信机制(互斥锁)】子系统开发

互斥锁 基本概念 互斥锁又称互斥型信号量&#xff0c;是一种特殊的二值性信号量&#xff0c;用于实现对共享资源的独占式处理。 任意时刻互斥锁的状态只有两种&#xff0c;开锁或闭锁。当任务持有互斥锁时&#xff0c;该互斥锁处于闭锁状态&#xff0c;这个任务获得该互斥锁…

从虚拟机安装CentOS到自定义Dockerfile构建tomcat镜像

写在开头 整个过程中涉及的三方软件均来源于三方的官网&#xff0c;因此需要有一个稳定良好的访问公网网络的环境&#xff0c;可能需要科学上网 下载并安装 VMware Workstation Player 下载 需要先注册登录&#xff1a;https://login.broadcom.com/signin 下载页面&#xff1a…

【IoTDB 线上小课 07】多类写入接口,快速易懂的“说明书”!

【IoTDB 视频小课】稳定更新中&#xff01;第七期来啦~ 关于 IoTDB&#xff0c;关于物联网&#xff0c;关于时序数据库&#xff0c;关于开源... 一个问题重点&#xff0c;3-5 分钟&#xff0c;我们讲给你听&#xff1a; 一条视频了解写入接口 了解我们的友友们&#xff0c;应该…