动态规划——线性dp

线性dp

概述

线性动态规划,是较常见的一类动态规划问题,其是在线性结构上进行状态转移,这类问题不像背包问题、区间dp等有固定的模板。线性动态规划的目标函数为特定变量的线性函数,约束是这些变量的线性不等式或等式,目的是求目标函数的最大值或最小值。

例题

数字三角形

给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

        7
      3   8
    8   1   0
  2   7   4   4
4   5   2   6   5

输入格式
第一行包含整数 n,表示数字三角形的层数。

接下来 n 行,每行包含若干整数,其中第 i 行表示数字三角形第 i 层包含的整数。

输出格式
输出一个整数,表示最大的路径数字和。

数据范围
1≤n≤500,
−10000≤三角形中的整数≤10000
输入样例:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例:
30

在这里插入图片描述

N = 510
INF = -50000010
a = [[0] * N for _ in range(N)]
f = [[INF] * N for _ in range(N)]

n = int(input())
for i in range(1, n + 1) :
	a[i][1:i + 1] = list(map(int, input().split()))

f[1][1] = a[1][1]
for i in range(2, n + 1) :
	for j in range(1, i + 1) :
		f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j]
res = INF
for i in range(1, n + 1) :
	res = max(res, f[n][i])
print(res)

最长上升子序列

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式
第一行包含整数 N。

第二行包含 N 个整数,表示完整序列。

输出格式
输出一个整数,表示最大长度。

数据范围
1≤N≤1000,
−109≤数列中的数≤109
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4

在这里插入图片描述

N = 1010
INF = - int(1e-9) - 7

nums = [INF] * N
f = [1] * N

n = int(input())

nums[1 :] = list(map(int, input().split()))

for i in range(1, n + 1) :
	for j in range(1, i) :
		if nums[i] > nums[j] :
			f[i] = max(f[i], f[j] + 1)
res = INF
for i in range(1, n + 1) :
	res = max(res, f[i])
print(res)

最长公共子序列

给定两个长度分别为 N 和 M 的字符串 A 和 B,求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少。

输入格式
第一行包含两个整数 N 和 M。

第二行包含一个长度为 N 的字符串,表示字符串 A。

第三行包含一个长度为 M 的字符串,表示字符串 B。

字符串均由小写字母构成。

输出格式
输出一个整数,表示最大长度。

数据范围
1≤N,M≤1000
输入样例:
4 5
acbd
abedc
输出样例:
3

在这里插入图片描述

N = 1010
f = [[0] * N for _ in range(N)]

n, m = map(int, input().split())
A = input()
B = input()

for i in range(1, n + 1) :
	for j in range(1, m + 1) :
		f[i][j] = max(f[i][j - 1], f[i - 1][j])
		if A[i - 1] == B[j - 1] :
			f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1)
print(f[n][m])

最长公共上升子序列

熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。

小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。

小沐沐说,对于两个数列 A 和 B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。

奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。

不过,只要告诉奶牛它的长度就可以了。

数列 A 和 B 的长度均不超过 3000。

输入格式
第一行包含一个整数 N,表示数列 A,B 的长度。

第二行包含 N 个整数,表示数列 A。

第三行包含 N 个整数,表示数列 B。

输出格式
输出一个整数,表示最长公共上升子序列的长度。

数据范围
1≤N≤3000,序列中的数字均不超过 231−1。

输入样例:
4
2 2 1 3
2 1 2 3
输出样例:
2
在这里插入图片描述

N = 3010
f = [[0] * N for _ in range(N)]

n = int(input())
A = list(map(int, input().split()))
B = list(map(int, input().split()))

for i in range(1, n + 1) :
	for j in range(1, n + 1) :
		f[i][j] = f[i - 1][j]
		if A[i - 1] == B[j - 1] :
			f[i][j] = max(f[i][j], 1)
			for k in range(1, j) :
				if B[k - 1] < B[j - 1] :
					f[i][j] = max(f[i][j], f[i - 1][k] + 1)
res = 0
for i in range(1, n + 1) :
	res = max(res, f[n][i])
print(res)

最短编辑距离

给定两个字符串 A 和 B,现在要将 A 经过若干操作变为 B,可进行的操作有:

删除–将字符串 A 中的某个字符删除。
插入–在字符串 A 的某个位置插入某个字符。
替换–将字符串 A 中的某个字符替换为另一个字符。
现在请你求出,将 A 变为 B 至少需要进行多少次操作。

输入格式
第一行包含整数 n,表示字符串 A 的长度。

第二行包含一个长度为 n 的字符串 A。

第三行包含整数 m,表示字符串 B 的长度。

第四行包含一个长度为 m 的字符串 B。

字符串中均只包含大小写字母。

输出格式
输出一个整数,表示最少操作次数。

数据范围
1≤n,m≤1000
输入样例:
10
AGTCTGACGC
11
AGTAAGTAGGC
输出样例:
4
在这里插入图片描述

N = 1010
f = [[0] * N for _ in range(N)]

n = int(input())
A = input()
m = int(input())
B = input()

for i in range(1, n + 1) :
	f[i][0] = i
for i in range(1, m + 1) :
	f[0][i] = i

for i in range(1, n + 1) :
	for j in range(1, m + 1) :
		f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1
		f[i][j] = min(f[i][j], f[i - 1][j - 1] + (A[i - 1] != B[j - 1]))

print(f[n][m])

总结

线性dp注重状态的表示和计算,对于遍历起点,涉及到状态转移方程有i-1索引的,一般从1开始
状态转移方程一维还是二维,除了根据经验外,一般采取从低维到高维的思考方式
一般经验是,如果是二维空间用二维,一个序列用一维,两个序列用二维
总结yxc和Carl的dp分析法
使用集合分析法,确定状态表示和状态计算方程
根据上面确定的两个,确定初始化
思考遍历顺序
手写一遍与打印对比debug

posted @   chanxe  阅读(145)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示