最长上升(下降)(不上升)(不下降)子序列,和dilworth定理

最长**子序列是动态规划中的经典问题,而dilworh定理也是其中重要的一环。

经典的问题那必然也就有经典的例题

P1020 [NOIP1999 普及组] 导弹拦截

题目中第一问要我们求最长不上升子序列。

具体的做法是:
假设我们现在有一个不上升序列 5 4 2 2

然后我们碰到了一个数字3,现在序列中的最后一个数比3小

但是当我们选择取3的话可以使得之后可以选取的数字更多(3,2,1)

所以我们考虑先不让这个序列增加,使用3将序列中第一个比3小的数字剔除掉

为什么可以剔除呢

因为剔除之后序列的长度并没有改变,而且当之后遇到小于2的数字的时候依然可以添加到队列的末尾

而要找到第一个比三小的数我们可以用upper_bound函数,其他的就看代码吧

#include<iostream>
#include<algorithm>
#include<cmath>
#include<string.h>
using namespace std;
int a[200005], n = 0, stac[200005], now = 0;
int main() {
    while (cin >> a[++n]); n--;
    for (int i = 1; i <= n; i++) {
        if (stac[now] >= a[i] || i == 1) {
            stac[++now] = a[i];
        }
        else {
            int x = upper_bound(stac + 1, stac + now + 1, a[i], greater<int>()) - stac;
            stac[x] = a[i];
        }
    }
    cout << now << endl;
    memset(stac, 0, sizeof(stac));
    now = 0;
    for (int i = 1; i <= n; i++) {
        if (stac[now] < a[i] || i == 1) {
            stac[++now] = a[i];
        }
        else {
            int x = lower_bound(stac + 1, stac + now + 1, a[i]) - stac;
            stac[x] = a[i];
        }
    }
    cout << now << endl;
    return 0;
}

而第二问中要我们求需要多少套设备该怎么办呢。

其实就是要我们求要求几次最长不上升子序列才能将导弹全部选完。

具体为什么我们不妨看看这篇博客    https://www.cnblogs.com/Gaomez/p/14324357.html

那要怎么求呢

我们可以借助dilworth定理

大概意思就是当我们要求一个最长**子序列数量的时候就是要求与其相反的最长子序列的长度。

比如这题我们要求最长不上升子序列数量,就是要求最长上升子序列的长度,明白了这个这题就很清楚了。

 

posted @ 2021-03-11 21:57  redintonc  阅读(175)  评论(0)    收藏  举报