求最长子序列——贪心法

求最长子序列——贪心法

  1. 思路:

    1. 维护一个数组 stk,要求这个数组里的元素在数值上是严格递增的。

      遍历每一个数,如果这个数比数组里的最后一个数更大,那么就将这个数插入数组的最后;反之,替换掉数组中第一个大于等于这个数的元素。

      最后的答案就是数组中元素的个数。

  2. 分析:

    1. 外部循环遍历n个数,时间复杂度为n;
    2. 循环内在维护数组stk内查找需要替换的下标数时,由于stk是单调递增的,所以采用二分法查找,时间复杂度为logn。
  3. 代码:

    // 最长上升子序列(贪心nlogn版)
    #include<bits/stdc++.h>
    
    const int N = 100010;
    int a[N], stk[N], cnt, n;
    /*
    
    int find(int x){
        int l = 1, r = cnt;
        while(l < r){ 
            int mid = l + r >> 1;
            if(stk[mid] >= x) r = mid;
            else l = mid;
        }
        return l + 1;
    }
    
    样例:10
    -5 -1 8 -4 3 -2 -5 -6 2 -6
    
    二分法这里要注意,比如以上的错误方法下,遭遇到诸如以下情况时:
    在stk数组-5, -4, 3中存入新数-2时会遇到
    
    l = 1, r = 3, mid = l + r >> 1 = 2;
    
    l = mid = 2, r = 3, mid = l + r >> 1 = 2;
    
    l = mid = 2, r = 3, mid = l + r >> 1 = 2;
    
    ......
    
    于是便发生了死循环。
    
    */
    
    int find(int x){
        int l = 1, r = cnt;
        while(l < r){ 
            int mid = l + r >> 1;
            if(stk[mid] >= x) r = mid;
            else l = mid + 1;
        }
        return l;
    }
    
    int main(){
        scanf("%d", &n);
    
        for(int i = 1; i <= n; i ++) scanf("%d", a + i);
        cnt = 1;
        stk[1] = a[1];
        for(int i = 2; i <= n; i ++){
            if(a[i] > stk[cnt]) stk[++ cnt] = a[i];
            else{
                // 开始二分查找:找到第一个大于a[i]的数,并将其替换掉。
                int x = find(a[i]);
                stk[x] = a[i];
            }
        }
        printf("%d", cnt);
        
        return 0;
    }
    
posted @ 2022-02-24 10:33  ture?  阅读(98)  评论(0编辑  收藏  举报