• 算法进阶 (LIS变形) 固定长度截取求最长不下降子序列【动态规划】【树状数组】


    先学习下LIS最长上升子序列

    ​ 看了大佬的文章OTZ:最长上升子序列 (LIS) 详解+例题模板 (全),其中包含普通O(n)算法*和以LIS长度及末尾元素成立数组的普通O(nlogn)算法,当然还有本文涉及的树状数组维护后的O(nlogn)算法*

    再贴一个容易理解的树状数组算法:https://www.cnblogs.com/war1111/p/7682228.html

    再看看这道题

    原题链接:http://acm.hnucm.edu.cn/JudgeOnline/problem.php?id=1373

    题目描述

    给定一个序列 a,求去除 a 中一段连续长度为 L 的序列后,a 的最长不下降子序列的长度的最大值。

    输入

    单组数据。
    第一行两个整数 n,L 表示序列的长度为 n,L 如题意所示。
    第二行 n 个数表示序列 a
    n ≤ 105, 0 ≤ L ≤ n

    输出

    输出一个整数表示最长不下降子序列长度的最大值

    样例输入

    6 3
    2 1 3 6 4 5

    样例输出

    3

    思路:假设数组 dp[i] 为 以 a[i] 结尾的LIS

    ​ dn[i] 为 以 a[i] 结尾并截取 l 长度后的最优LIS

    状态转移:

    ​ 1、首先应该想到在 i < L 时 dn[i] = 0;

    ​ 2、其次考虑 i>=L

    ​ ① 截取的 l 长度为 a[i-L] ~ a[i-1] 时,只需考虑把 a[i] 加在 a[1] ~ a[i-L-1] 后面,即满足条件的 dp[1] ~ dp[i-L-1] 中的最大值。 dn[i] = max { dp[j]+1 } (1 <= j < i - L , A[j] < A[i] )

    ​ ② 截取的 l 长度在 ①条件 之前,那么这个时候是不是可以在 dn 数组本身去找,因为前面的dn就是截取后的呀,即满足条件的 dn[1] ~ dn[i-1]中的最大值。 dn[i] = max { dn[j]+1 } (1 <= j < i , A[j] < A[i] )

    ​ 最后 ans 就是 dni中的最大值 <截取在 i 之前> 以及 dpi中最大<截取在 i 之后>。

    dp思路类似于 蓝桥杯——最大的算式

    代码实现

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn = 1e5;
    int a[maxn+5],dp[maxn+5],dn[maxn+5];
    int main()
    {
        int n,k,ans=0;
        scanf("%d%d",&n,&k);
        for(int i=0; i<n; i++){
            scanf("%d",&a[i]);
        }
    
        //求 dp[i] , O(n^2)
        for(int i=0; i<n; i++){
            dp[i] = 1;
            for(int j=0; j<i; j++){
                if(a[i] >= a[j]){
                    dp[i] = max(dp[i],dp[j]+1);
                }
            }
        }
        //求dn[i]
        for(int i=0; i<n; i++){
            dn[i] = 0;
            if(i>=k){
                for(int j=0; j<i-k; j++)
                    if(a[i] >= a[j])
                        dn[i] = max(dn[i],dp[j]+1);
                for(int j=k; j<i; j++){
                    if(a[i] >= a[j]){
                        dn[i] = max(dn[i],dn[j]+1);
                    }
                }
                ans = max(ans, dn[i]);
            }
        }
    
        printf("%d\n",ans);
        return 0;
    }
    

    点击并拖拽以移动

    ​ 相信思路应该很清晰吧,对于之前做 dp 都是读完题万年懵,一看题解知天下,这道题当时能把状态转移搞清楚真的很有成就感,BUT 超时超时超时!!!

    其实思路不变,按照LIS树状数组思路去做,对应的 dn[i] 也开一个对应的树状数组存储,用到了离散化,当输入值比较大则会超出树状数组范围,但是输入数量一定,就按照输入值的大小顺序及关系(大于,小于,等于)重新赋值覆盖输入值。

    在hnucm平台上这道题提交了20多次测试,感谢不杀之恩orz:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5+10;
    int t[2][maxn],dp[maxn],dn[maxn],n,L,ans=0;
    struct Node {int val,num;} a[maxn];
    bool cmp(Node a,Node b){
        return a.val<b.val;
    }
    bool cmp1(Node a,Node b){
        return a.num<b.num;
    }
    
    
    // T[x] 表示目前以数值 x 结尾的最大长度;t[0][x]对应dp[i]的树状数组,t[1][x]对应dn[i]的树状数组
    int query(int p,int x){
        int cnt = 0;
        while(x)  cnt=max(cnt,t[p][x]), x-=x&-x;
        return cnt;
    }
    int update(int p,int x,int m){
        while(x<=n) t[p][x]=max(t[p][x],m), x+=x&-x;
    }
    int main()
    {
        scanf("%d%d",&n,&L);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i].val);
            a[i].num = i;
        }
        sort(a+1,a+1+n,cmp);
        a[1].val = 1;
        for(int i=2,k=1;i<=n;i++){
            if(a[i].val==a[i-1].val) a[i].val = k;
            else a[i].val = ++k;
        }
        sort(a+1,a+1+n,cmp1);
        for(int i=1; i<=n; i++){
            dp[i] = query(0,a[i].val) +1;   //每次遍历的时候用 dp[i] 去查询更新当前的 T[a[i]]
            update(0,a[i].val,dp[i]);
            if(i>L){
                dn[i] = query(1,a[i].val) +1;
                update(1,a[i].val,dn[i]);   //在已截取的基础上寻找LIS
                update(1,a[i-L].val,dp[i-L]);   //截取当前元素的前L个
                ans = max(ans,dn[i]);
            }
        }
        for(int i=1;i<=n-L;i++){    //如果截取的L在最优LIS后面,取出来没有截取的就行
            ans=max(ans,dp[i]);
        }
        printf("%d\n",ans);
        return 0;
    }
    
  • 相关阅读:
    怎样才能充分利用SQL索引
    MS SQL存储过程编写经验和优化措施
    ASP調用存講過程總結
    調用外部的DLL(DllImportAttribute)
    MS SQL中的行轉列
    SQL Server乐观锁定和悲观锁定实例
    如何使用 JScript 從 HTML 網頁自動化 Excel
    了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密
    C#語法學習結構(Struct)
    四项技术 助你提高SQL Server的性能
  • 原文地址:https://www.cnblogs.com/betternow/p/13383375.html
Copyright © 2020-2023  润新知