今天遇到了几个LIS的题目, 整得我焦头烂额, 不过终于算是搞懂了, 下面分享给大家, 并推荐几篇讲的非常好的博客, 以及几道非常好的练习题做
动态规划
首当其冲的方法当然非动态规划莫属, 这种方法也是非常容易理解的一种方法, 首先定义一个数组f[]
, f[i]
的值代表以a[i]结尾的最长上升序列的长度, 状态转移的核心代码如下所示
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
//如果a[i]比a[j]大, 那么把a[i]接在以a[j]结尾的序列后面当然就更长啦
if (a[i] > a[j]) {
a[i] = max(a[i], a[j] + 1);
}
}
//ans保存最终的答案
ans = max(ans, a[i]);
}
复制代码
利用了upper_bound的二分查找的O(nlogn)的算法
这个算法有点难以理解, 反正蒟蒻我花了好久才搞懂, 感觉这个有点像贪心算法, 正确性不会证明, 我们想要得到一个最长的上升序列, 那么我们肯定希望长度越长越好, 结尾元素越小越好
(因为越小的话后面能接的元素当然就越多啦).
实现的方式
首先我们构造一个栈s[]
, 这个栈的长度保证是最优解的长度, 但里面的元素值可就不保证喽, 原因下面我会交代, 就是这里卡了我好久
如果a[i] >= s[len],说明a[i]可以接在s后面(而整个s还是有序的),那就简单粗暴地把a[i]丟进s, 否则的话
那么就说明这个比栈顶元素小的元素k接不上去, 但可能含这个元素的上升序列会更优, 毕竟它更小, 能接在它后面的元素会更多嘛, 于是我们就在栈s
中利用upper_bound
函数去二分寻找第一个大于k的元素, 假设它是y, 然后用k替换y. 看到这, 您可能会很疑惑, 为什么可以替换???
这里解释原因, 如果y在序列的末尾, 那么把它给踢了当然没毛病, 很容易想, 但是如果y不在末尾呢? 这时把它踢了对最优解的长度是没有影响的, 因为你替换后得到的序列的长度不会小于替换前
博客推荐
接下来推荐几篇不错的博客帮助大家消化一下