• LIS——最长上升子序列


    处理何种问题:已知一个原序列,求出其最长的一个子序列,且该子序列是单调递增的。(对于其子序列的要求条件是,可以不连续,但是先后顺序不可改变)

    性能:普通DP为O(n^2),贪心+二分为O(n*logn),DP+树状数组O(n*logn)。

    原理: 贪心+二分解释不出原理,网上的资料个人感觉不是很有说服力; DP类的状态转移方程为: dp[i]=max{dp[j]+1}(1<=j<i,arr[j]<arr[i])

    实现步骤:在这里写一下后两种算法的。

    贪心+二分:对于一个上升的子序列,显然其结尾元素越小,越有利于在后面接其他的元素,也就越有可能变得更长。开一个数组arr,用于存放原始数列,依次取出每一个数,放入一个是原始是空的有序数组path里面,用lower_bound();求出该数在path里可以刚好替换原位置,且path依旧可以保持升序的位置,例如:

    原序列为1,5,8,3,6,7

    Path已为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终path为1,3,6,7。最长递增子序列为长度4。

    该方法我也只能理解到这里了,每个数在path里的位置,个人感觉就是dp[i],但是这种解法有点绕,严谨的证明逻辑还得自己以后想。

    DP+树状数组维护:这个方法我喜欢讲

    状态转移方程为: dp[i]=max{dp[j]+1}(1<=j<i,arr[j]<arr[i])

    这个方程比较好想出来,但是有一点不好处理,就是在1~i里面找最大的dp[j],且arr[j]<arr[i],区间找最大值见过,线段树嘛,但是有限制条件的找最大值我就没见过了。怎么做呢?先将原始序列从小到大排序,并且保存好原数列的角标,然后对于每一个数边查找,边入树,比如第一个数的值是1,位置是100,因为原始dp数组里的值都是0,所以在dp[1~99]里找到的最大值就是0,所以dp[100]=1,然后将dp[100~n]的最大值更新一遍(树状数组更新),然后再进行第二个数,假设是5,位置是120,步骤还是和上面一样,之所以这么做的原因是,因为之前排序之后已经处理掉arr[i]>arr[j]的这个条件了,然后再用边查找,边枚举的方法,就可以很好的处理掉这个问题了。有些算法原理就是自然而然,不用纠结,记住这种处理策略就好。

    备注:重点把这种处理最大值的方法记住。

    输入样例解释

    8 //n个数字

    9 5 6 9 2 15 2 5

    输出样例解释

    4 //最长上升子序列的长度

    //LIS 有三种方法求解,第一种是常规 DP,时间复杂度是O(n^2);第二种是贪心+二分 时间复杂度是O(nlogn);
    //第三种是DP树状数组维护,时间复杂度是O(nlogn)。在这里依次写下来:
    
    /*
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    const int MaxN=10010;//数据量最大
    int arr[MaxN],dp[MaxN];
    
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            for(int i=1;i<=n;++i)   scanf("%d",&arr[i]);
            for(int i=1;i<=n;++i)   dp[i]=1;//注意:dp初始值是1,因为每个数都可以选取自身
    
            for(int i=1;i<=n;++i)
            {
                for(int j=1;j<i;++j)
                {
                    if(arr[i]>arr[j])
                        dp[i]=max(dp[i],dp[j]+1);
                }
            }
    
            int ans=0;
            for(int i=1;i<=n;++i)
                ans=max(ans,dp[i]);
    
            printf("%d
    ",ans);
        }
        return 0;
    }
    */
    
    /*
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    const int MaxN=10000000;//大致的极限空间复杂度,同时也大概是极限时间复杂度
    int arr[MaxN];
    int path[MaxN];//贪心时所用到的辅助数组
    
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            memset(arr,0,sizeof(arr));
            memset(path,0,sizeof(path));
    
            for(int i=1;i<=n;++i)   scanf("%d",&arr[i]);
    
            int tot=0;
            for(int i=1;i<=n;++i)
            {
                int num=lower_bound(path+1,path+tot+1,arr[i])-path;
                path[num]=arr[i];
                tot=max(tot,num);
            }
    
            printf("%d
    ",tot);
        }
    
        return 0;
    }
    
    */
    
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define lowbit(x) (x&(-x))
    const int MaxN=10000;
    int n;
    
    struct node
    {
        int val,num;
    };
    node arr[MaxN];
    int dp[MaxN];
    bool cmp(node a,node b)
    {
        if(a.val<b.val||(a.val==b.val&&a.num<b.num))
            return 1;
        return 0;
    }
    
    int modify(int x,int y)
    {
        for(; x<=n; x+=lowbit(x))
            dp[x]=max(dp[x],y);
    }
    
    
    int query(int x)
    {
        int res=0;
        for(; x; x-=lowbit(x))
            res=max(res,dp[x]);
        return res;
    }
    
    
    int main()
    {
        int ans;
        while(~scanf("%d",&n))
        {
            ans=0;
            for(int i=1; i<=n; ++i)
            {
                scanf("%d",&arr[i].val);
                arr[i].num=i;
                dp[i]=0;
            }
            sort(arr+1,arr+n+1,cmp);
            for(int i=1; i<=n; ++i)
            {
                int maxx=query(arr[i].num);
                modify(arr[i].num,++maxx);
                ans=max(ans,maxx);
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    

      

  • 相关阅读:
    51nod 1463 找朋友 (扫描线+线段树)
    51nod 1295 XOR key (可持久化Trie树)
    51nod 1494 选举拉票 (线段树+扫描线)
    51Nod 1199 Money out of Thin Air (树链剖分+线段树)
    51Nod 1287 加农炮 (线段树)
    51Nod 1175 区间中第K大的数 (可持久化线段树+离散)
    Codeforces Round #426 (Div. 1) B The Bakery (线段树+dp)
    前端基础了解
    git 教程
    HIVE 默认分隔符 以及linux系统中特殊字符的输入和查看方式
  • 原文地址:https://www.cnblogs.com/l1l1/p/9562600.html
Copyright © 2020-2023  润新知