线性动态规划
《算法设计与分析》期末复习
最优子结构的证明
这篇以 01 背包为例,证明其具有最优子结构性质
导弹拦截
定义状态 \(f(i)\) 为 \(1 ~ i\) 区间内的最大导弹拦截数量
有状态转移
直接赤裸裸的做状态转移,时间复杂度预计在 \(O(n^2)\),空间复杂度在 \(O(n)\)
考虑对于一组序列 $ { a_1, a_2, ..., a_n} $
维护的状态数组中提取出的最长不下降子序列为 $ {1, 1, ..., 2, ..., k} $
实际上对于状态数组中最后一段连续的值相同的序列,选择这段序列中任何一个对应的导弹作为结尾(也就是最后一个打下来的导弹)都合法,但是选择最高的那个必然是最优的
那么可以定义 $ cnt(1 ... num) $ 记录我们的答案,显而易见的是 $ cnt $ 具备一个不怎么严格的单调性
那么对于下标 $ j $,如果 $ a(j) <= a(cnt(num)) $,则 $ f(j) = f(cnt(num)) + 1, cnt(num + 1) = a(j)$
否则二分查找最大的元素 \(a(cnt(k))\) 满足 $ a(cnt(k)) <= a(j) $,把 $ cnt(k) $ 替换为 $ j $;也就是说,我打下来 \(k\) 个导弹,最后一个打下来的导弹不再是之前那个矮的了,而是现在这个高一点的
最后答案就是 \(num\)
第二问涉及到 Dilworth 定理在偏序集上的证明
打鼹鼠
容易证明,起点必然在某一个鼹鼠的出生位
容易想到以下状态转移
空间上太复杂,算法实现上也不做人,必然涉及到记忆化搜索,考虑简化状态转移
实际上,对于题目的需求,坐标本身描述空间的属性意义不大,仅仅规定了从一个状态到另一个状态之间转移要付出的代价
完全可以将二维点抽象成按时间排列的一维的序列,两两之间的转移代价由 $ | x_i - x_j | + | y_i - y_j | $ 唯一确定
那么这题又变成了最长子序列的问题了,只不过不再是由“不下降”或者“不上升”这样的关系规定,而是由 $ | x_i - x_j | + | y_i - y_j | < k_i - k_j $ 这样的关系来规定
那么有状态转移:
初始状态需要枚举起点:
快速求和
容易想到01背包,在做dp之前用 \(O(len^2)\) 求出任意区间组成的数字大小,定义 $ f(j) $ 为容量为 $ j $ 时的最小东西个数
在w(i) 指向的数字序列和 f(j-w(i)) 指向的数字序列没有交叉时,有状态转移:
至于怎么解决这个判定问题,可以朴素求解,在更新 $ f(j) $ 的时候顺带保存对应的 \(index\) 序列,比如 \(123\) 这种,在更新时对而两者各自的序列做一下判断
或者从状态入手,在状态的表示中引入位置信息 $ i $(实际上就是01背包最开始的做法),令 \(f(i, j)\) 表示以 \(i\) 结尾,和为 \(j\) 的最小加法次数
编辑距离
有前面的经验,这个题就很简单了
定义 \(f(i, j)\) 为 \(a_1 ... a_i\) 和 \(b_1 .. b_j\) 相同的所需最少操作
考虑 \(f(i, j)\) 从 \(f(i-1, j)\)转移过来的话,需要删除 \(a_i\),次数 + 1;$ f(i, j) $ 从 \(f(i-1, j-1)\) 转移过来的话,如果不同的话需要做变换,次数 + 1
为啥上面的状态转移看上去没有插入的情况捏,因为删除和插入实际上是一组对偶操作
那么有状态转移
合唱队形
定义状态 \(f(i, k)\) 表示 \(a_1, ..., a_i, ..., a_k\) 的最长合唱队列长度
那 \(f(i, i)\) 怎么办捏,再提前做一遍最长上升子序列,\(f(i, i)\) 表示最长上升子序列长度
这么做有点蠢
可以直接统计最长上升子序列长度和最长下降子序列长度,枚举中点就好
樱花
典型的混合背包
怎么例题也是这个,乐
回文字串
注意到字符串长度不大于 \(1000\),意味着 \(O(n^2)\) 的代价是可以承受的
设 \(f(i, j)\) 表示把 \(a_i, ..., a_j\) 变成回文串的最小代价
很显然有如下状态转移:
在实际编写时可能会有这么一个疑惑:究竟是枚举起点还是枚举长度,也即最外层循环到底是什么?
联系前面几道题以及离散数学中学过的 floyd,可以发现决定枚举最外层的因素是阶段而非状态内的变量i或者j
floyd 为什么枚举中转点,因为中转点代表了一个阶段,下一个阶段的计算需要用到这个阶段的计算信息
对于最长子序列问题为什么枚举i,因为当前阶段指 \(a_1, ..., a_i\),下一个阶段指 \(a_1, ..., a_{i+1}\)
回到这题,不难发现实际上要枚举当前计算的区间长度 \(k\)
过河卒
讲实话,根据这个课期中考试的沟槽难度我觉得会考这个
深搜广搜不讲了
琪露诺
观察数据,发现 $ N <= 2 \times 10 ^ 5$,复杂度基本确定在 \(O(nlogn)\)
首先确认一件事:不走回头路,不然状态的转移会变得有些复杂
对于一个点 \(i\),可以由区间内 \((i-R, i-L)\) 的格子转移过来
显然有状态转移
问题是这个max函数,如果用遍历的方式求解显然不可取
考虑维护一个优先队列,每次取队头的时候判断这个东西过期了没,如果过期了就丢掉,否则采纳这个队头
大师
设 \(f(i, k)\) 为以 \(i\) 结尾,公差为 \(k\) 的选择方案总数
有如下状态转移:
具体实现的时候 \(k\) 直接由 \(a_i - a_j\) 决定
实际上用 \(f(i, j)\) 表示以 \(a_i\) 作为第一个塔,\(a_i, ..., a_j\) 形成的方案数的状态表示也不是不行;但是要额外记录等差数列的这个差值,实现上有点困难;把约束条件记入状态会更容易实现转移
最长公共子序列
怎么对 \(O(n^2)\) 不友好了 😦
状态没办法简化了,从状态转移入手
感觉没办法入手啊操 😦
怎么变 Hash 了 😦 ?
没做出来,抄一下别人的做法
使用哈希的方法,把任意一组转变成不下降的序列;将同样的映射关系作用到另一组上,这样问题就转化成求一个最长不下降序列的问题了,见导弹拦截
我操 😦 ?