图解算法:LIS问题,单调队列+二分优化

图解算法:LIS问题,单调队列+二分优化_第1张图片

关注下方公众号,分享硬核知识

作者 | 小K

出品 | 公众号:小K算法 (ID:xiaok365)

01

故事起源

LIS:Longest Increasing Subsequence(最长递增子序列)。
给你一个整数数组,如何求出其中最长的严格递增子序列的长度?

图解算法:LIS问题,单调队列+二分优化_第2张图片

比如下面绿色和蓝色都是符合要求的子序列,但蓝色的子序列更长,所以最大长度为4。

图解算法:LIS问题,单调队列+二分优化_第3张图片

02

思考

要求最长子序列的长度,那肯定要先找出这个最长的子序列。比如下面蓝色的子序列就是我们要找的序列。

图解算法:LIS问题,单调队列+二分优化_第4张图片

所以整个数组的元素就可以分为两类,一类是属于最终的目标序列,一类是不属于最终的目标序列。
那我们要怎么确定一个元素是否应该选取到目标序列呢?咱们还是从小规模开始分析吧,懂的都懂(小K式思维法)。

图解算法:LIS问题,单调队列+二分优化_第5张图片

03

小规模分析

3.1

一个元素

如果只有一个元素,那自然是要选了,最大长度为1。

图解算法:LIS问题,单调队列+二分优化_第6张图片

3.2

两个元素

如果再加第2个元素,满足递增子序列,两个都选,最大长度为2。

图解算法:LIS问题,单调队列+二分优化_第7张图片

如果再加第2个元素是这样的,选了第1个就选不了第2个,不选第1个就可以选第2个。但怎么选都只能选1个,最大长度是1。

图解算法:LIS问题,单调队列+二分优化_第8张图片

3.3

三个元素

如果选取了前面2个元素,再加第3个元素,已经无法再选取,最大长度为2。

图解算法:LIS问题,单调队列+二分优化_第9张图片

如果前面只选取第1个元素,再加第3个元素,可以选取第3个元素,最大长度也为2。

图解算法:LIS问题,单调队列+二分优化_第10张图片

请问你发现什么规律了吗?

图解算法:LIS问题,单调队列+二分优化_第11张图片

因为是递增序列,所以一个元素能不能选,跟之前选择的最后一个元素有关。如果大于最后一个元素就可以选择,如果小于或等于就无法选择。

04

抽象分析

如果不考虑任何规律,每个元素可以选或不选, 个元素就有 种选取方法。当然要保证递增序列,就可以排除很多不合理的情况,但规模依然很大。

图解算法:LIS问题,单调队列+二分优化_第12张图片

假设原问题已经找到了最大的子序列,现在我再给你加一个元素进来,选还是不选呢?

图解算法:LIS问题,单调队列+二分优化_第13张图片

如果原问题最大长度为X,那选择最新的元素的前提就是最大长度能变成X+1,如果选了长度没变甚至更小那肯定就不选。说明新的元素一定要能接在之前的最优解后面才可选取。
数据规模每扩大一次,最优解也是在之前的最优解上面扩充。

图解算法:LIS问题,单调队列+二分优化_第14张图片

再根据第3小节的分析,对于每一个最优解,其实我们并不关注前面的X-1个元素是怎么选择的,只关注第X个元素是选的啥,那就可以从小规模开始枚举,求出以每个元素作为结尾时的最大长度,这样就可以递推出更大规模的最优解,这其实就是动态规划的思想。

05

动态规划

设 表示以第 个元素为结尾,能得到的最大的长度,那么 一定是从之前的某个最优解递推过来。
即。

图解算法:LIS问题,单调队列+二分优化_第15张图片

代码实现

void dp() {
    int a[100], f[100], ans = 0;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        f[i] = 1;
        for (int j = 0; j < i; ++j) {
            if (a[j] < a[i]) {
                f[i] = max(f[i], f[j] + 1);
            }
        }
        ans = max(ans, f[i]);
    }
    cout << ans << endl;
}

动态规划的复杂度为 ,当 很大时效率就很低了,那能不能继续优化呢,这就还得继续找本质规律。

06

哪里有问题呢

6.1

长度相等,高度更矮

如果前4个元素是这样的:
选取前2个长度为2,选取后2个长度也是2,但明显选取后2个更好,因为末位的高度更矮。
如果再加一个元素15,那就可以接在后面变成更大的长度3,但如果选择前面两个就不行。说明前面2个可以直接抛弃。

图解算法:LIS问题,单调队列+二分优化_第16张图片

6.2

长度更长,高度更矮

如果前5个元素是这样的:
选取前2个长度为2,选取后3个长度为3,而且高度更矮,明显更优。说明前面2个也应该直接抛弃。

图解算法:LIS问题,单调队列+二分优化_第17张图片

6.3

长度更长,高度更高

如果前3个元素是这样的:
选取前2个长度为2,选取第3个长度为1。虽然前面的高度更高了,但长度也更大,不能直接抛弃。
比如再来一个元素18,就可以直接接在前面2个之后,长度为3。

图解算法:LIS问题,单调队列+二分优化_第18张图片

6.4

公式分析

再回到之前的DP递推公式,在求每一个 时,其实都是从之前找一个符合要求的最大的 。之前的方法是枚举每一个,这样查找自然效率就比较低,那能否更快的找到我们想要的 呢?

图解算法:LIS问题,单调队列+二分优化_第19张图片

6.1~6.3分析了不同的情况,其实我们只需要保留6.3中的情况,长度更长,末位更高。把这些单独拿出来再看,其实就是一个单调上升队列,长度和高度同步单调递增。对于一个单调有序的队列,你会怎么查找呢,当然最快就是二分查找了啊。

图解算法:LIS问题,单调队列+二分优化_第20张图片

再看上面的 和 其实可以放在一起,这时 的含义就变成长度为 时末尾的最小值,我们只需要维护这个一维队列即可。

图解算法:LIS问题,单调队列+二分优化_第21张图片

6.5

如何维护

从左开始扫描每一个数,如果当前数 等于队尾的数则直接抛弃。
如果当前数 大于列尾的数,就直接加入队尾。

图解算法:LIS问题,单调队列+二分优化_第22张图片

如果当前数 小于列尾的数,就通过二分查找,找到刚好大于 的数 ,并用 替换 。

图解算法:LIS问题,单调队列+二分优化_第23张图片

这样最终的 队列的长度就是我们要找的最长递增子序列长度。

07

算法实现

在通过二分查找时,要注意几个细节,就是 指针最终停留的位置有不同的情况。

如下 。

图解算法:LIS问题,单调队列+二分优化_第24张图片

如下 。

图解算法:LIS问题,单调队列+二分优化_第25张图片

如下 。

图解算法:LIS问题,单调队列+二分优化_第26张图片

所以在更新的时候需要判断。

代码实现

void greed() {
    int f[1000], x, num = 0;
    f[0] = 0;
    for (int i = 0; i < n; ++i) {
        cin >> x;
        if (x == f[num]) {
            continue;
        }
        if (x > f[num]) {
            f[++num] = x;
            continue;
        }
        int l = 0, r = num, mid;
        while (l < r) {
            mid = (l + r) / 2;
            if (f[mid] == x) {
                l = mid;
                break;
            }
            if (f[mid] < x) {
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        //最后停留位置,>,<,=都有可能,小于更新当前位置,大于更新后一位置
        if (x < f[l]) { f[l] = x; }
        else if (x > f[l]) {
            f[l + 1] = x;
        }
    }
    cout << num << endl;
}

08

总结

这是一道比较经典的动态规划问题,公式也很简单。但不论是通过原问题模拟过程的分析,还是直接通过递推公式分析,都可以发现有很多冗余的操作,这些就是我们优化的切入点,单调队列在DP中是很常用的优化技巧,而单调有序性就可以结合二分查询提高效率。以后可以再给大家讲讲其它的几种常用DP优化技巧。

本文原创作者:小K,一个思维独特的写手。
文章首发平台:微信公众号【小K算法】。

如果喜欢小K的文章,请点个关注,分享给更多的人,小K将持续更新,谢谢啦!

图解算法:LIS问题,单调队列+二分优化_第27张图片

关注下方公众号,分享硬核知识

图解算法:LIS问题,单调队列+二分优化_第28张图片

关注我,涨知识

图解算法:LIS问题,单调队列+二分优化_第29张图片

原创不易,感谢分享

转发,点赞,在看

往期精彩回顾

算法面试题:均分纸牌

图解算法:冒泡排序

巴什博弈:取石子游戏

图解算法:LIS问题,单调队列+二分优化_第30张图片

分享给更多朋友,转发,点赞,在看

你可能感兴趣的:(算法,队列,xhtml,gwt,payment)