题目连接:http://poj.org/problem?id=2533c++
Time Limit: 2000MS Memory Limit: 65536Kspa
Descriptioncode
Inputblog
Outputip
Sample Inputci
7 1 7 3 5 9 4 8
Sample Outputelement
4
题意:get
给定长度为 $N$ 的一串整数序列 $a[1 \sim N]$,求其最长上升子序列的长度。input
注意:子序列能够不连续,要求严格单增。it
题解:
$O(n \log n)$ 解法——贪心+二分。
构建一个栈 $S$ 和一个变量 $top$ 表明栈顶位置,该栈的表明:栈中的第 $i$ 个数 $S[i]$,是序列 $a$ 中,长度为 $i$ 的递增子序列的末尾元素。
初始化 S[top=1]=a[1] ,即将第一个数字入栈;这很好理解,到目前为止 $a[1]$ 本身是一个长度为 $1$ 的递增子序列。
遍历 $a[ i = 2 \sim N ]$:每次对于 $a[i]$,找出栈 $S[1 \sim top]$ 中第一个大于等于 $a[i]$ 的数的位置 $pos$,若不存在则返回 $pos=top+1$。
这是因为,若存在第一个大于等于 $a[i]$ 的数 $S[pos]$ ,说明对于长度为 $pos$ 的递增子序列,能够用 $a[i]$ 代替掉其原来的末尾元素 $S[pos]$,这样一来,依然是一个长度为 $pos$ 的递增子序列,并且该递增子序列被进一步“加长”的潜力增长。而若是栈中不存在大于等于 $a[i]$ 的数,这说明我能够在目前长度为 $top$ 的递增子序列后面加上一个 $a[i]$,那么咱们就获得了一个以 $a[i]$ 为结尾的,长度为 $top+1$ 的递增子序列。
所以,咱们把 $S[pos]$ 更新为 $a[i]$,而且尝试更新栈的大小 if(pos>top) top=pos; 。
因为栈 $S$ 中元素始终保持单调递增(并且栈内元素互不相等),因此找 $S$ 中第一个大于等于 $a[i]$ 的数能够使用二分查找。
AC代码(在OpenJudge百练提交):
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+5; int n; vector<int> a; int S[maxn],top; int LIS(const vector<int>& a) { S[top=0]=a[0]; for(int i=0;i<a.size();i++) { int pos=lower_bound(S,S+top+1,a[i])-S; S[pos]=a[i], top=max(top,pos); } return top+1; } int main() { cin>>n; while(n--) { int x; cin>>x; a.push_back(x); } cout<<LIS(a)<<endl; }
PS.咱们能够看到,求第一个大于等于 $a[i]$ 的数使用了lower_bound,相应的若是咱们使用upper_bound会怎么样呢?不难证实,咱们将会获得最长不降低子序列的长度。