洛谷P1020(dp)

https://www.luogu.org/problemnew/show/P1020

题意:

让你维护一个非升的序列和一个单调升序的序列

思路:

一个 O ( n l o g n ) O(nlogn) O(nlogn)的算法(最长单调上升子序列)

首先:

对一个有序的序列
l o w e r _ b o u n d ( b e g i n , e n d , k e y ) − b [ ] : 在 b e g i n 到 e n d − 1 中 , 查 找 到 第 一 个 大 于 等 于 k e y 的 数 字 的 位 置 lower\_bound(begin, end, key)-b[]:在begin到end-1中,查找到第一个大于等于key的数字的位置 lower_bound(begin,end,key)b[]:beginend1key
u p p e r _ b o u n d ( b e g i n , e n d , k e y ) − b [ ] : 在 b e g i n 到 e n d − 1 中 , 查 找 到 第 一 个 大 于 k e y 的 数 字 的 位 置 upper\_bound(begin, end, key)-b[]:在begin到end-1中,查找到第一个大于key的数字的位置 upper_bound(begin,end,key)b[]:beginend1key
l o w e r _ b o u n d ( b e g i n , e n d , k e y , g r e a t e r < i n t > ( ) ) − b [ ] : 在 b e g i n 到 e n d − 1 中 , 查 找 到 第 一 个 小 于 等 于 k e y 的 数 字 的 位 置 lower\_bound(begin, end, key, greater<int>())-b[]:在begin到end-1中,查找到第一个小于等于key的数字的位置 lower_bound(begin,end,key,greater<int>())b[]:beginend1key
u p p e r _ b o u n d ( b e g i n , e n d , k e y , g r e a t e r < i n t > ( ) ) − b [ ] : 在 b e g i n 到 e n d − 1 中 , 查 找 到 第 一 个 小 于 k e y 的 数 字 的 位 置 upper\_bound(begin, end, key, greater<int>())-b[]:在begin到end-1中,查找到第一个小于key的数字的位置 upper_bound(begin,end,key,greater<int>())b[]:beginend1key
l o w e r _ b o u n d 和 u p p e r _ b o u n d 只 有 一 个 等 于 的 差 别 lower\_bound和upper\_bound只有一个等于的差别 lower_boundupper_bound

然后:

对于一组数,当我们已经遍历到 i i i的时候,我们已经得到了一个最长的子序列 b [ l e n ] = x 1 , x 2 . . . x l e n b[len]={x_1,x_2...x_{len}} b[len]=x1,x2...xlen,如果 a [ i ] > b [ l e n ] a[i]> b[len] a[i]>b[len],那么乐意直接 b [ + + l e n ] = a [ i ] b[++len]=a[i] b[++len]=a[i],如果 a [ i ] ≤ b [ l e n ] a[i]\leq b[len] a[i]b[len],那我们可以通过修改 b b b中第一个大于等于 a [ i ] a[i] a[i]的数字来使得 b b b数组更有竞争力,
b [ l o w e r _ b o u n d ( b + 1 , b + 1 + l e n , a [ i ] ) − b ] = a [ i ] b[lower\_bound(b+1,b+1+len,a[i])-b]=a[i] b[lower_bound(b+1,b+1+len,a[i])b]=a[i],这样我们持续更新这个 b b b数组,就可以得到一个最长上升子序列
为什么是 l o w e r _ b o u n d ? lower\_bound? lower_bound
因为是单调上升,所以不存在有相同的元素,所以可以修改 b b b中的相同元素,
而如果是不单调的话,那么就不用修改相同的元素,就用 u p p e r _ b o u n d upper\_bound upper_bound
代码:

b[1] = h[0];
len = 1;
 for (int i = 1; i < cnt; i ++) {
     if(h[i] > b[len]) b[++len] = h[i];
     else {
         int pos = lower_bound(b+1, b+1+len, h[i]) - b;
         b[pos] = h[i];
     }
 }

最长非升子序列:
与最长上升同理,我们只需要修改查找规则,因为是非升所以不用修改相同的元素,所以用 u p p e r _ b o u n d upper\_bound upper_bound

b[1] = h[0];
int len = 1;
for (int i = 1; i < cnt; i ++) {
    if(h[i] <= b[len]) b[++len] = h[i];
    else {
        int pos = upper_bound(b+1, b+1+len, h[i], greater<int>()) - b;
        b[pos] = h[i];
    }
}

AC代码:

#include

using namespace std;

#define LL long long
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const int Mod = 1e9 + 7;
const double eps = 1e-8;
typedef pair<int, int> psi;

void fre() {
    if(freopen("洛谷P1020.txt", "r", stdin) == NULL)
        system("touch 洛谷P1020.txt");
    freopen("洛谷P1020.txt", "r", stdin);
}

int h[maxn];
int dp[maxn], b[maxn];

int main(int argc, char *args[]) {
    fre();
    int cnt = 0, x;
    while(scanf("%d", &x) != EOF) {
        h[cnt++] = x;
    } 
    b[1] = h[0];
    int len = 1;
    for (int i = 1; i < cnt; i ++) {
        if(h[i] <= b[len]) b[++len] = h[i];
        else {
            int pos = upper_bound(b+1, b+1+len, h[i], greater<int>()) - b;
            b[pos] = h[i];
        }
    }
    printf("%d\n", len);
    b[1] = h[0];
    len = 1;
    for (int i = 1; i < cnt; i ++) {
        if(h[i] > b[len]) b[++len] = h[i];
        else {
            int pos = lower_bound(b+1, b+1+len, h[i]) - b;
            b[pos] = h[i];
        }
    }
    printf("%d\n", len);
    return 0;
}

你可能感兴趣的:(题解,dp)