线性DP与真题

news2024/12/23 3:55:43

目录

一、前言

二、最长公共子序列(lanqiaoOJ题号1189,类似于1054)

三、最长递增子序列

1、蓝桥骑士(lanqiaoOJ题号1188)

四、编辑距离

1、字符串转换(lanqiaoOJ题号1507)

五、网络图上的DP

1、过河卒(lanqiaoOJ题号755)

六、其他例题

1、排列数(2019年国赛,lanqiaoOJ题号240)

1)暴力法

2)DP

2、砝码称重(2021年省赛,lanqiaoOJ题号1447)

3、数字三角形(2020年省赛,lanqiaoOJ题号505)


一、前言

本文主要讲了关于线性DP的一些例题。

二、最长公共子序列(lanqiaoOJ题号1189,类似于1054)

【题目描述】

给定一个长度为 n 数组 A 和一个长度为 m 的数组 B。请你求出它们的最长公共子序列长度为多少。

【输入描述】

输入第一行包含两个整数 n、m。第二行包含 n 个整数 ai,第三行包含 m 个整数 bi, 1≤n, m≤10^3, 1≤ai, bi≤10^9。

【输出描述】

输出一行整数表示答案。

  • 一个给定序列的子序列,是在该序列中删去若干元素后得到的序列。例如:X = {A, B, C, B, D, A,  B}、它的子序列有 {A, B, C, B, A}、{A, B,D}、{B, C, D, B} 等。
  • 给定两个序列 X 和 Y,当另一序列 Z 既是 X 的子序列又是 Y 的子序列时,称 Z 是序列 X 和 Y 的公共子序列。
  • 最长公共子序列是长度最长的子序列。
  • 暴力法:先找出 A 的所有子序列,然后一一验证是否为 Y 的子序列。
  • 如果 A 有 m 个元素,那么 A 有 2^m 个子序列;B 有 n 个元素;总复杂度大于 O(n2^m)。

【动态规划】

dp[i][j]:序列 Ai(a1~ai) 和 Bj(b1~bj)的最长公共子序列的长度。答案: dp[n][m]

分解为 2 种情况:

1)当 ai=bj 时,已求得 Ai-1 和 Bj-1 的最长公共子序列,在其尾部加上 ai 或 bj 即可得到 Ai 和 Bj  的最长公共子序列。状态转移方程:dp[i][j]=dp[i-1][j-1]+1

2)当 ai≠bj 时,求解两个子问题:Ai-1 和 Bj 的最长公共子序列;Ai 和 Bj-1 的最长公共子序列。取最大值,状态转移方程:dp[i][j]=max{dp[i][j-1], dp[i-1][j]}

3)复杂度 O(nm)

上述解释通俗易懂啊!

【用交替滚动数组】

1)当 ai=bj 时,状态转移方程:dp[i][j]=dp[i-1][j-1]+1

2)当 ai≠bj 时,状态转移方程:dp[i][j]=max{dp[i][j-1], dp[i-1][j]}

n,m=map(int,input().split())
a=[0]+list(map(int,input().split()))
b=[0]+list(map(int,input().split()))
dp=[[0]*(m+1) for _ in range(2)]    #注意这里是m
now=0
old=1
for i in range(1,n+1):
    now,old=old,now
    for j in range(1,m+1):
        dp[now][j]=max(dp[now][j-1],dp[old][j])
        if a[i]==b[j]:
            dp[now][j]=max(dp[now][j],dp[old][j-1]+1)
print(dp[now][m])

三、最长递增子序列

1、蓝桥骑士(lanqiaoOJ题号1188)

【题目描述】

小明是蓝桥王国的骑士,他喜欢不断突破自我。这天蓝桥国王给他安排了 N 个对手,他们的战力值分别为 a1, a2, ..., an,且按顺序阻挡在小明的前方。对于这些对手小明可以选择挑战,也可以选择避战。身为高傲的骑士,小明从不走回头路,且只愿意挑战战力值越来越高的对手。请你算算小明最多会挑战多少名对手。

【输入描述】

第一行是整数 N,表示对手的个数,第二行是 N 个整数 a1, a2, ..., an,表示对手战力值。1 ≤ N ≤ 3×10^5

【输出描述】

输出一行整数表示答案。

【分析】

给定一个长度为 n 的数组,找出一个最长的单调递增子序列。

例:序列 A={5, 6, 7, 4, 2, 8, 3},它最长的单调递增子序列为{5, 6, 7, 8},长度为 4。

定义状态dp[i]:表示以第 i 个数为结尾的最长递增子序列的长度。

状态转移方程:dp[i] = max{dp[j]} + 1,0<j<i,Aj<Ai

答案:max{dp[i]}

复杂度:j 在 0~i 之间滑动,复杂度 O(n);i 的变动范围也是 O(n) 的;总复杂度 O(n^2)。

动态规划:复杂度 O(n^2)

本题 n<=3×10^5,DP 代码提交到 OJ 会超时。

DP 不是 LIS 问题的最优解法,有复杂度 O(nlogn) 的非DP解法

这题就此打住。

四、编辑距离

1、字符串转换(lanqiaoOJ题号1507)

【题目描述】

给定两个单词 A 和 B,计算出将 A 转换为 B 所需的最小操作数。一个单词允许进行以下 3 种操作: 1)插入一个字符;2)删除一个字符;3)替换一个字符。

【输入描述】

输入第一行包含一个字符串 S。输入第二行包含一个字符串 T。1<=|S|, IT|<=2×10^3,保证 S、T 只包含小写字母。

【输出描述】

输出一个整数表示答案。

【分析】

  • 把长度 m 的 A 存储在数组 a[1]~a[m],长度为 n 的 B 存储在 b[1]~b[n],不用 a[0] 和 b[0]。
  • 定义状态 dp:dp[i][j] 表示 A 的前 i 个字符转换 B 的前 j 个字符所需要的操作步骤
  • dp[m][n] 就是答案

下图是 A="abcf",B="bcfe" 的状态转移矩阵。

状态转移方程:

1)若 a[i] = b[j],则 dp[i][j] = dp[i-1][j-1]。例如图中 dp[2][1] 处的箭头。

2)其他情况:dp[i][j] = min{dp[i-1][j-1], dp[i-1][j], dp[i][j-1]} + 1。例如图中 dp[4][2] 处的箭头。dp[i][j] 是它左、左上、上的三个值中的最小值加1,分别对应以下操作:

  • dp[i-1][j]+1,删除,将 A 的最后字符删除;
  • dp[i][j-1]+1,插入,在 B 的最后插入A的最后字符;
  • dp[i-1][j-1]+1,替换,将 B 的最后一个字符替换为 A 的最后一个字符。

复杂度:0(mn)。

a=input()
a=' '+a         #a[0]不用
b=input()
b=' '+b
m=len(a)-1
n=len(b)-1
dp=[[0]*(n+1) for _ in range(m+1)]
for i in range(1,m+1):
    dp[i][0]=i
for j in range(1,n+1):
    dp[0][j]=j
for i in range(1,m+1):
    for j in range(1,n+1):
        if a[i]==b[j]:
            dp[i][j]=dp[i-1][j-1]
        else:
            dp[i][j]=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1
print(dp[m][n])

五、网络图上的DP

1、过河卒(lanqiaoOJ题号755)

【题目描述】

棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为 “马拦过河卒”。现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。棋盘用坐标表示,A 点 (0, 0)、B 点 (n,m),同样马的位置坐标是需要给出的。1<=n, m<=20,0<=马的坐标<=20。

【输入格式】

一行四个正整数,表示 B 点坐标和马的坐标。

【输出格式】

一个整数,表示所有的路径条数。

  • 统计路径条数,看起来是个搜索题,可以用 DFS 求解。把马的控制点标记为不走,绕过它们。不过,用 DFS 搜索的路径数量是天文数字,肯定超时。
  • 在小学上过奥数的都知道,这题应该用 “标数法”,就是在每个坐标点上记录能走的路径条数。
  • 标数法实际上就是DP的递推。

【DP】

  • 定义状态 dp[][]:dp[i][j] 表示卒走到坐标 (i, j) 时能走的路径条数。
  • 如果不考虑马的控制点,有:dp[i][j]=dp[i-1][j] + dp[i][j-1];
  • 也就是 (i, j) 点的路径条数等于它上面和左边的路径条数之和。这就是小学奥数的 “标数法” 的原理。
  • 本题的限制条件是马的控制点,只要令控制点的 dp[i][j]=0 即可,即这个点上无路径。

小技巧:把坐标加2,防止马的控制点越界。

dp=[[0]*25 for i in range(25)]
s=[[0]*25 for i in range(25)]
bx,by,mx,my=map(int,input().split())
bx+=2
by+=2
mx+=2
my+=2
dp[2][1]=1
s[mx][my]=1
s[mx-2][my-1]=1
s[mx-2][my+1]=1
s[mx+2][my-1]=1
s[mx+2][my+1]=1
s[mx-1][my-2]=1
s[mx-1][my+2]=1
s[mx+1][my-2]=1
s[mx+1][my+2]=1
for i in range(2,bx+1):
    for j in range(2,by+1):
        if s[i][j]==1:
            dp[i][j]=0
        else:
            dp[i][j]=dp[i-1][j]+dp[i][j-1]
print(dp[bx][by])

这类题其实是最好做了。

六、其他例题

1、排列数(2019年国赛,lanqiaoOJ题号240)

【题目描述】

在一个排列中,一个折点是指排列中的一个元素,它同时小于两边的元素,或者同时大于两边的元素。对于一个 1~n 的排列,如果可以将这个排列中包含 t 个折点,则它称为一个 t+1 单调序列。例如,排列 (1, 4, 2, 3) 是一个 3 单调序列,其中 4 和 2 都是折点。给定 n 和 k,请问 1~n 的所有排列中有多少个 k 单调队列?

【输入描述】

输入一行包含两个整数 n, k (1<=k<=n<=500)。

【输出描述】

输出一个整数表示答案。答案可能很大,你可需要输出满足条件的排列数量除以 123456 的余数即可。

1)暴力法

20% 的测试: 1<=k<=n<=10

暴力法:对所有排列进行检查,判断是否为 k 单调队列。

from itertools import *
n,k=map(int,input().split())
nums=[i for i in range(1,n+1)]      #1~n
cnt=0
for num in permutations(nums):      #检查每个排列
    tmp=0
    for i in range(n-2):
        if num[i+1]>num[i+2] and num[i+1]>num[i]:
            tmp+=1                  #凸折点
        elif num[i+1]<num[i+2] and num[i+1]<num[i]:
            tmp+=1                  #凹折点
    if tmp==k-1:
        cnt+=1
print(cnt%123456)

2)DP

定义dp[][]:dp[i][j] 表示序列包含 1~i,且排列为 j 单调队列的方案数,也就是含有 j-1 个折点的方案数。

答案: dp[n][k]

状态转移方程:

从 dp[i-1][] 递推到 dp[i][],把 i 插入到 1~i-1 的一个排列中,折点数量的变化:

dp[i][j] = dp[i-1][j]*j + dp[i-1][j-1]*2 + dp[i-1][j-2]*(i-j)

(怎么退出状态转移方程是关键)

N=520
dp=[[0]*N for i in range(N)]
n,k=map(int,input().split())
dp[1][1]=1
dp[2][1]=2
for i in range(3,n+1):
    ki=min(k,i)
    for j in range(1,ki+1):
        dp[i][j]+=dp[i-1][j]*j+dp[i-1][j-1]*2
        if j>1:
            dp[i][j]+=dp[i-1][j-2]*(i-j)
print(dp[n][k]%123456)

2、砝码称重(2021年省赛,lanqiaoOJ题号1447)

【题目描述】

你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2, ······, WN。请你计算一共可以称出多少种不同的重量?注意砝码可以放在天平两边。

【输入描述】

输入的第一行包含一个整数 N。第二行包含 N 个整数: W1, W2, W3, ······, WN。

【输出描述】

输出一个整数代表答案。对于所有评测用例,1≤N≤100, N 个砝码总重不超过 100000。

【DP】

建模:给定 n 个正整数,从中选出若个数字组合,每个数字可以加或者减,最终能得到多少种正整数结果。

DP状态定义:dp(i, j) 表示前 i 个数字选择若干个加或者减,能否获得和为 j。

DP状态转移方程:dp(i, j)=dp(i-1, j)|dp(i-1, j-wi)|dp(i-1, j+wi)

状态转移方程中有三种情况:

dp(i-1, j):不用第 i 个数字,和为 j

dp(i-1, j-wi):用第 i 个数字,且做减法,等价于用 i-1 个数字实现 j-wi;

dp(i-1, j+wi):用第 i 个数字,且做加法,等价于用 i-1 个数字实现 j+wi;

这一题也可以直接模拟,并且用 set 判重。

n=int(input())
w=list(map(int,input().split()))
ans=set()
ans.add(w[0])
for i in w[1:]:
    for j in ans.copy():
        ans.add(i)
        ans.add(j+i)
        if j-i!=0:
            ans.add(abs(j-i))
print(len(ans))

3、数字三角形(2020年省赛,lanqiaoOJ题号505)

【题目描述】

图中给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右边的那个数。此外,向左下走的次数与向右下走的次数相差不能超过 1。

【输入格式】

输入的第一行包含一个整数 N(1<N <= 100),表示三角形的行数。下面的 N 行给出数字三角形。数字三角形上的数都是 0 至 100 之间的整数。

【输出格式】

输出一个整数,表示答案。

【输入】

5

7

3 8

8 1 0

2 7 4 4

4 5 2 6 5

【输出】

27

本题要求向左走的次数与向右走的次数差值不超过 1,当到达最后一层时,一定是落在中间位置。

如果层数是奇数,最后一层落在正中间元素上;

如果层数是偶数,最后一层落在第 N/2 或第 N/2+1 个元素上。

定义状态 dp[][],dp[i][j] 表示从顶部到第 i 层横向第 j 个数,最大路径和。它只能从上一层的左边或右边转移而来。

n=int(input())
a=[list(map(int,input().split())) for i in range(n)]
#数组a[][]同时当成dp[][]用
for i in range(1,n):
    for j in range(0,i+1):
        if j==0:
            a[i][j]+=a[i-1][j]      #最左边元素
        elif j==i:
            a[i][j]+=a[i-1][j-1]    #最右边元素
        else:
            a[i][j]+=max(a[i-1][j-1:j+1])
if n&1:
    print(a[-1][n//2])
else:
    print(max(a[-1][n//2-1],a[-1][n//2]))

补充练习题: 

以上,线性DP与真题

祝好

 

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

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

相关文章

JavaScript两数之和

两数之和 两层for循环 // O(n^2) const twoNum function(nums,target){for(let i 0;i<nums.length;i){for(let ji1 ;j<nums.length;j){if(nums[i]nums[j]target){return[i,j]}}} }双指针 // 当数组为有序的时候O(n) const twoNum2 function(nums,target){let i 0 …

SpringCloud学习

由于Spring Cloud基于Spring Boot构建&#xff0c;而Spring Cloud Alibaba又基于Spring Cloud Common的规范实现&#xff0c;所以当我们使用Spring Cloud Alibaba来构建微服务应用的时候&#xff0c;需要知道这三者之间的版本关系。 目前Spring Cloud Alibaba的版本与Spring Bo…

1-1MySql复习

MySql复习 一 数据类型 数值 字符串 ​ char(5) 定长字符串 varchar(5) 可变长度字符串 日期 ​ timestamp 记录行数据的最后修改事件 二 基本查询 1 聚合函数 avg count sum max min 2 排序 order by ​ asc ​ desc 3 分组 group by … having … 分组通常跟…

Python语言的重要性(模式识别与图像处理课程作业)

Python语言的重要性&#xff08;模式识别与图像处理课程作业&#xff09;Python语言的重要性1 Python的优点主要有&#xff1a;1.1、简单1.2、易学1.3、速度快1.4、免费1.5、高层语言1.6、解释性1.7、面向对象1.8、可扩展性1.9、可嵌入性1.10、丰富的库1.11、规范的代码2 Pytho…

TCP/IP网络编程——套接字的多种可选项

完整版文章请参考&#xff1a; TCP/IP网络编程完整版文章 文章目录第 9 章 套接字的多种可选项9.1 套接字可选项和 I/O 缓冲大小9.1.1 套接字多种可选项9.1.2 getsockopt & setsockopt9.1.3 SO_SNDBUF & SO_RCVBUF9.2 SO_REUSEADDR9.2.1 发生地址分配错误&#xff08;B…

高效学 C++|编程实例之计算器

本节将实现一个能进行实数间加、减、乘、除运算的简易计算器。首先创建一个基于QWidget带界面的Qt项目&#xff0c;然后按照如下步骤进行操作&#xff1a; 01、计算器界面设计 在界面中拖入两个单行文本框和十七个按钮&#xff0c;按钮上显示的文字、按钮对象和单行文本框对象…

百分百拿捏offer的自动化测试面试题全套教程

最近很多咨询我&#xff0c;有没有软件测试方面的面试题&#xff0c;尤其是Python自动化测试相关的最新面试题&#xff0c;所以今天给大家整理了一份&#xff0c;希望能帮助到你们。 接口测试基础 1、公司接口测试流程是什么&#xff1f; 从开发那边获取接口设计文档、分析接口…

VUE3 指令 插槽

指令 指令是 Vue 模板语法里的特殊标记&#xff0c;在使用上和 HTML 的 data-* 属性十分相似&#xff0c;统一以 v- 开头&#xff08; e.g. v-html &#xff09;。 它以简单的方式实现了常用的 JavaScript 表达式功能&#xff0c;当表达式的值改变的时候&#xff0c;响应式地…

1x1卷积、Inception网络

目录1.1x1卷积(1x1 convolution)又称网络中的网络(network in network)池化层只能压缩图像的宽和高&#xff0c;1x1卷积能压缩通道数量&#xff0c;减少计算成本。如上图&#xff0c;输入维度的通道数为192&#xff0c;用32个1x1x192的filters&#xff0c;就能将输出的通道数压…

java基础—面试题一

文章目录1.和equals区别是什么&#xff1f;2.Java中的 <<、>>、>>> 是什么3.if-else-if-else与switch的区别4.while和do-while的区别5.switch 是否能作用在 byte 上&#xff0c;是否能作用在 long 上&#xff0c;是否能作用在String上6.&和&&…

大数据技术架构(组件)16——Hive:内置UDTF函数

1.4.11、内置UDTF函数1.4.11.1、explodeselect explode(array(100,200,300));Array<int> myCol[100,200,300][400,500,600]得到的结果如下&#xff1a;(int) myNewCol1002003004005006001.4.11.2、posexplodeselect posexplode(array(A,B,C));1.4.11.3、parse_url_tuples…

2023云原生安全值得关注的3个方向

如果说过去几年教会了我们什么的话&#xff0c;那就是云原生和开源环境中安全的重要性。 Log4j 等漏洞产生的重大影响&#xff0c;在无数的行业中浮现&#xff0c;对于云原生环境中的其他安全问题也越来越受到重视。 组织不再质疑是否要迁移到云端&#xff0c;而是在寻找最快、…

centos下安装docker 并通过docker安装gitlab

一:安装docker1、若之前安过docker&#xff0c;可以先卸载yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-selinux \docker-engine-selinux \docker-engine \docker-ce2、更新yum…

软件测试基础(四) 之 软件测试的覆盖率

一、什么是软件测试的覆盖率&#xff1f;软件测试覆盖率是软件测试技术有效性的一个度量手段&#xff0c;用来度量测试完整性。意思概括的说&#xff0c;软件测试的工作中会有非常非常多的item&#xff08;任务&#xff09;&#xff0c;执行过的任务和总任务数的一个比值&#…

尚医通 (二)项目搭建

目录一、工程结构介绍1、工程结构2、模块说明二、创建父工程1、创建sprigboot工程yygh_parent2、删除 src 目录3、配置 pom.xml4、在pom.xml中添加依赖的版本三、搭建model模块1、在父工程yygh_parent下面创建模块model2、添加项目需要的依赖3、复制项目实体类和VO类四、搭建se…

require和important区别

1.require是赋值过程&#xff0c;就是把一个值赋值给另一个&#xff0c;important是对这个值的引用 2 . require 是赋值过程并且是运行时才执行&#xff0c;也就是同步加载&#xff0c;import 是解构过程并且是编译时执行&#xff0c;理解为异步加载 3.require 的性能相对于 im…

Linux部署达梦数据库超详细教程

陈老老老板&#x1f9b8;&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;国产数据库-达梦数据库&#xff08;主要讲一些达梦数据库相关的内容&#xff09;&#x1f468;‍&#x1f4bb;本文简述&#xff1a;本文讲一下达梦数据库的下载与安装教程&#xff08;Linux版&#x…

百度网盘秒传链接生成及提取方法

百度网盘秒传链接生成及提取方法 1.认识秒传链接 首先&#xff0c;我们认识一下秒传链接的格式&#xff1a; 秒传链接是由标准提取码文件名组成。例如下面的格式&#xff1a; fd00338387f50ee5919eb3df4cfce6e3#5048587008#/影视/电影/救火奶爸.mp4 百度网盘秒传链接的提取主…

FISSURE:一款功能强大的RF和逆向工程框架

关于FISSURE FISSURE是一款功能强大的RF和逆向工程框架&#xff0c;该工具适用于不同技能水平的安全研究人员&#xff0c;并提供了信号检测、信号分类、协议发现、渗透测试、IQ操作、漏洞分析、自动化和AI/机器学习等功能。该框架旨在促进软件模块、无线电、协议、信号数据、脚…

2023年怎么开通一个抖音小店?店铺开通后做什么?开店指南!

大家好&#xff0c;我是王路飞。 2023年都已经过去一个月了&#xff0c;你开通抖音小店了吗&#xff1f; 作为目前最受欢迎的创业和副业项目&#xff0c;开通抖音小店的商家数量与日俱增&#xff0c;都是为了蹭一下抖音流量的红利&#xff0c;毕竟直播带货如今正处在风口。 …