单串

单串 dp[i] 线性动态规划最简单的一类问题,输入是一个串,状态一般定义为 dp[i] := 考虑[0..i]上,原问题的解,其中 i 位置的处理,根据不同的问题,主要有两种方式:

  第一种是 i 位置必须取,此时状态可以进一步描述为 dp[i] := 考虑[0..i]上,且取 i,原问题的解;
  第二种是 i 位置可以取可以不取

大部分的问题,对 i 位置的处理是第一种方式,例如力扣:

70 爬楼梯问题
801 使序列递增的最小交换次数
790 多米诺和托米诺平铺
746 使用最小花费爬楼梯
线性动态规划中单串 dp[i] 的问题,状态的推导方向以及推导公式如下

1. 依赖比 i 小的 O(1) 个子问题
dp[n] 只与常数个小规模子问题有关,状态的推导过程 dp[i] = f(dp[i - 1], dp[i - 2], ...)。时间复杂度 O(n),空间复杂度 O(n) 可以优化为 O(1),例如上面提到的 70, 801, 790, 746 都属于这类。

如图所示,虽然绿色部分的 dp[i-1], dp[i-2], ..., dp[0] 均已经计算过,但计算橙色的当前状态时,仅用到 dp[i-1],这属于比 i 小的 O(1)个子问题。

例如,当 f(dp[i-1], ...) = dp[i-1] + nums[i] 时,当前状态 dp[i] 仅与 dp[i-1] 有关。这个例子是一种数据结构前缀和的状态计算方式,关于前缀和的详细内容请参考下一章。

2. 依赖比 i 小的 O(n) 个子问题
dp[n] 与此前的更小规模的所有子问题 dp[n - 1], dp[n - 2], ..., dp[1] 都可能有关系。

状态推导过程如下:

dp[i] = f(dp[i - 1], dp[i - 2], ..., dp[0])

依然如图所示,计算橙色的当前状态 dp[i] 时,绿色的此前计算过的状态 dp[i-1], ..., dp[0] 均有可能用到,在计算 dp[i] 时需要将它们遍历一遍完成计算。

其中 f 常见的有 max/min,可能还会对 i-1,i-2,...,0 有一些筛选条件,但推导 dp[n] 时依然是 O(n)O(n) 级的子问题数量。

例如:

  139 单词拆分
  818 赛车
以 min 函数为例,这种形式的问题的代码常见写法如下

for i = 1, ..., n

  for j = 1, ..., i-1
    dp[i] = min(dp[i], f(dp[j])
时间复杂度 O(n^{2}),空间复杂度 O(n)

单串 dp[i] 经典问题
以下内容将涉及到的知识点对应的典型问题进行讲解,题目和解法具有代表性,可以从一个问题推广到一类问题。

1. 依赖比 i 小的 O(1) 个子问题
53. 最大子数组和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

一个数组有很多个子数组,求哪个子数组的和最大。可以按照子数组的最后一个元素来分子问题,确定子问题后设计状态

dp[i] := [0..i] 中,以 nums[i] 结尾的最大子数组和
状态的推导是按照 i 从 0 到 n - 1 按顺序推的,推到 dp[i],时,dp[i - 1], ..., dp[0] 已经计算完。因为子数组是连续的,所以子问题 dp[i] 其实只与子问题 dp[i - 1] 有关。如果 [0..i-1] 上以 nums[i-1] 结尾的最大子数组和(缓存在 dp[i-1] )为非负数,则以 nums[i] 结尾的最大子数组和就在 dp[i-1] 的基础上加上 nums[i] 就是 dp[i] 的结果否则以 i 结尾的子数组就不要 i-1 及之前的数,因为选了的话子数组的和只会更小。

按照以上的分析,状态的转移可以写出来,如下

dp[i] = nums[i] + max(dp[i - 1], 0)
这个是单串 dp[i] 的问题,状态的推导方向,以及推导公式如下

dp[i] = f(dp[i - 1], dp[i - 2], ..., dp[0])

在本题中,f(dp[i-1], ..., dp[0]) 即为 max(dp[i-1], 0) + nums[i],dp[i] 仅与 dp[i-1] 1 个子问题有关。因此虽然绿色部分的子问题已经计算完,但是推导当前的橙色状态时,只需要 dp[i-1] 这一个历史状态。

2. 依赖比 i 小的 O(n) 个子问题
300. 最长上升子序列
给定一个无序的整数数组,找到其中最长上升子序列的长度。

输入是一个单串,首先思考单串问题中设计状态 dp[i] 时拆分子问题的方式:枚举子串或子序列的结尾元素来拆分子问题,设计状态 dp[i] := 在子数组 [0..i] 上,且选了 nums[i] 时,的最长上升子序列。

因为子序列需要上升,因此以 i 结尾的子序列中,nums[i] 之前的数字一定要比 nums[i] 小才行,因此目标就是先找到以此前比 nums[i] 小的各个元素,然后每个所选元素对应一个以它们结尾的最长子序列,从这些子序列中选择最长的,其长度加 1 就是当前的问题的结果。如果此前没有比 nums[i] 小的数字,则当前问题的结果就是 1 。

本题依然是单串 dp[i] 的问题,状态的推导方向,以及推导公式与上一题的图示相同,

状态的推导依然是按照 i 从 0 到 n-1 推的,计算 dp[i] 时,dp[i-1], dp[i-2], ..., dp[0] 依然已经计算完。

但本题与上一题的区别是推导 dp[i] 时,dp[i-1]. dp[i-2], ..., dp[0] 均可能需要用上,即,因此计算当前的橙色状态时,绿色部分此前计算过的状态都可能需要用上。

单串相关练习题
最经典单串 LIS 系列
最大子数组和系列
打家劫舍系列
变形:需要两个位置的情况
与其它算法配合
其它单串 dp[i] 问题
带维度单串 dp[i][k]
股票系列

动态规划精讲(一)A单串的更多相关文章

  1. 动态规划精讲(一)LC 最长递增子序列的个数

    最长递增子序列的个数 给定一个未排序的整数数组,找到最长递增子序列的个数. 示例 1: 输入: [1,3,5,4,7]输出: 2解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, ...

  2. 动态规划精讲(一)LC最长公共子序列

    P1439 [模板]最长公共子序列 题目描述 给出1,2,-,n 的两个排列P1​ 和P2​ ,求它们的最长公共子序列. 输入格式 第一行是一个数 n. 接下来两行,每行为 n 个数,为自然数 1,2 ...

  3. 第三百四十三节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy模拟登陆和知乎倒立文字验证码识别

    第三百四十三节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy模拟登陆和知乎倒立文字验证码识别 第一步.首先下载,大神者也的倒立文字验证码识别程序 下载地址:https://gith ...

  4. 深入Java核心 Java内存分配原理精讲

    深入Java核心 Java内存分配原理精讲 栈.堆.常量池虽同属Java内存分配时操作的区域,但其适用范围和功用却大不相同.本文将深入Java核心,详细讲解Java内存分配方面的知识. Java内存分 ...

  5. 《VC++ 6简明教程》即VC++ 6.0入门精讲 学习进度及笔记

    VC++6.0入门→精讲 2013.06.09,目前,每一章的“自测题”和“小结”三个板块还没有看(备注:第一章的“实验”已经看完). 2013.06.16 第三章的“实验”.“自测题”.“小结”和“ ...

  6. Linux实战教学笔记12:linux三剑客之sed命令精讲

    第十二节 linux三剑客之sed命令精讲 标签(空格分隔): Linux实战教学笔记-陈思齐 ---更多资料点我查看 1,前言 我们都知道,在Linux中一切皆文件,比如配置文件,日志文件,启动文件 ...

  7. 【原创】分布式之redis复习精讲

    引言 为什么写这篇文章? 博主的<分布式之消息队列复习精讲>得到了大家的好评,内心诚惶诚恐,想着再出一篇关于复习精讲的文章.但是还是要说明一下,复习精讲的文章偏面试准备,真正在开发过程中, ...

  8. 转 Redis 总结精讲 看一篇成高手系统-4

    转 Redis 总结精讲 看一篇成高手系统-4 2018年05月31日 09:00:05 hjm4702192 阅读数:125633   本文围绕以下几点进行阐述 1.为什么使用redis 2.使用r ...

  9. 总结:Java 集合进阶精讲1

    知识点:Java 集合框架图 总结:Java 集合进阶精讲1 总结:Java 集合进阶精讲2-ArrayList 集合进阶1---为集合指定初始容量 集合在Java编程中使用非常广泛,当容器的量变得非 ...

随机推荐

  1. 老杜告诉你java小白到大神是怎么炼成的(转载)

    老杜告诉你java小白到大神是怎么炼成的 1. 学习前的准备 一个好的学习方法(应该怎么学习更高效): 一个合格的程序员应该具备两个能力 有一个很好的指法速度(敲代码快) 有一个很好的编程思想(编程思 ...

  2. Map的putAll方法验证

    下面的程序验证了Map的putAll方法的行为特性,代码如下: import java.util.HashMap; public class Map_putAllTest { public stati ...

  3. How to name a slf4j logger

    Use logger in a non-static context: Logger logger = LoggerFactory.getLogger(this.getClass().getName( ...

  4. Git-04-本地仓库撤销修改

    编辑修改了文件,但是还没有git add之前 直接用 git checkout -- filename 这个命令就可以了 已经 git add 了,但是没有 git commit 之前 1 模拟git ...

  5. java8-stream常用操作(1)

    前言 java8的Stream 流式操作,用于对集合进行投影.转换.过滤.排序.去重等,更进一步地说,这些操作能链式串联在一起使用,类似于 SQL 语句,可以大大简化代码.下面我就将平时常用的一些st ...

  6. nohup 启动命令

    start.sh #!/bin/bash nohup $PWD/node_exporter > /dev/null 2>&1 &

  7. 【工作篇】了解升级 Spring 版本导致的跨域问题

    一.背景 最近需要统一升级 Spring 的版本,避免 common 包和各个项目间的 Spring 版本冲突问题.这次升级主要是从 Spring 4.1.9.RELEASE 升级到 Spring 4 ...

  8. NOIP 模拟 $11\; \rm english$

    题解 本题有一定代码难度 对于需要区间最大值,可以反过来考虑,先预处理出每个数所能扩展的最大边界,也就是说,求出一个最大的区间,其最大值为这个数,单调栈 \(\mathcal O(n)\) 求解 那么 ...

  9. 题解 matrix

    传送门 无比毒瘤的dp题,而且伪装地好像很可做的样子 考场上我给它氪了差不多一个小时最后还是只能扔了个20pts状压走人 以下思路基本均来源于题解: 对于此题,题面中三个限制条件: (1)第 i 行第 ...

  10. 公司新来了一个质量工程师,说团队要保证 0 error,0 warning

    摘要:静态代码检查又称为静态程序分析,是指在不运行计算机程序的条件下,进行程序分析的方法. 本文分享自华为云社区<公司新来了一个质量工程师,说团队要保证 0 error,0 warning> ...