求最长子序列——贪心法
求最长子序列——贪心法
-
思路:
-
维护一个数组 stk,要求这个数组里的元素在数值上是严格递增的。
遍历每一个数,如果这个数比数组里的最后一个数更大,那么就将这个数插入数组的最后;反之,替换掉数组中第一个大于等于这个数的元素。
最后的答案就是数组中元素的个数。
-
-
分析:
- 外部循环遍历n个数,时间复杂度为n;
- 循环内在维护数组stk内查找需要替换的下标数时,由于stk是单调递增的,所以采用二分法查找,时间复杂度为logn。
-
代码:
// 最长上升子序列(贪心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; }