描述
一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).
你的任务,就是对于给定的序列,求出最长上升子序列的长度.输入
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
输出
最长上升子序列的长度。
样例
- Input
7
1 7 3 5 9 4 8- Output
4
解法
法一:普通dp。用dp[i]以a[i]为结尾的最长上升子序列,则状态转移方程为:
dp[i]=max(dp[i],dp[j]+1)(0<=j < i,a[i]>a[j])
时间复杂度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#include<bits/stdc++.h> #define INIT(a,b) memset(a,b,sizeof(a)) #define LL long long int using namespace std; const int MAX=0x7fffffff; const int MIN=-0x7fffffff; const int INF=0x3f3f3f3f; const int Mod=1e9+7; const int MaxN=1e7+7; int main(){ //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); int n,a[1005],dp[1005]; cin>>n; for(int i=0;i<n;i++){ cin>>a[i]; dp[i]=1; } int ans=1; for(int i=1;i<n;i++) for(int j=0;j<i;j++) if(a[i]>a[j]){ dp[i]=max(dp[j]+1,dp[i]); ans=max(dp[i],ans); } cout<<ans<<endl; return 0; }
法二:dp+二分。用dp[i]表示长为i的上升串中最小的末尾数,用len记录当前上升子序列的最大长度,当处理a[i]时,先比较a[i]和dp[len]的大小,如果a[i]>dp[len],则dp[++len]=a[i];如果a[i]<=dp[len],则在dp序列中找到第一个比a[i]大的最小末位数并用a[i]替换掉。时间复杂度.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#include<bits/stdc++.h> #define INIT(a,b) memset(a,b,sizeof(a)) #define LL long long int using namespace std; const int MAX=0x7fffffff; const int MIN=-0x7fffffff; const int INF=0x3f3f3f3f; const int Mod=1e9+7; const int MaxN=1e7+7; int N,len,a[1005]; int dp[1005];//dp[i]表示长为i的上升串中最小的末尾数 int main(){ //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); cin>>N; for(int i=1;i<=N;i++){ cin>>a[i]; dp[i]=INF; } dp[1]=a[1],len=1; for(int i=1;i<=N;i++){ if(a[i]>dp[len])dp[++len]=a[i]; else *upper_bound(dp+1,dp+N+1,a[i])=a[i];//找到第一个比a[i]大的最小末尾数并替换 } cout<<len<<endl; return 0; }
法三:dp+树状数组。在的DP中,dp[i]=max(dp[j])+1(0<=j < i,a[i]>a[j])。现在我们用树状数组来维护dp[j]的最大值(a[i]>a[j]),这样的话查找max(dp[j])的复杂度就从O(n)降到了O(lgn).但是我们在用树状数组处理之前,对a数组进行排序(注意先保存各个数的位置)。时间复杂度O(nlgn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
#include<bits/stdc++.h> #define INIT(a,b) memset(a,b,sizeof(a)) #define LL long long int using namespace std; const int MAX=0x7fffffff; const int MIN=-0x7fffffff; const int INF=0x3f3f3f3f; const int Mod=1e9+7; const int MaxN=1e7+7; int dp[1005],N; struct Num{ int val,key; bool operator < (Num& e)const{return val==e.val?key>e.key:val<e.val;} //数值相同序号大的放前面,防止相等 }num[1005]; int lowbit(int x){return x&-x;} void update(int x,int New){ for(;x<=N;x+=lowbit(x)) dp[x]=max(dp[x],New); } int que(int x){ int res=MIN; for(;x;x-=lowbit(x)) res=max(res,dp[x]); return res; } int main(){ //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); cin>>N; for(int i=1;i<=N;i++){ cin>>num[i].val; num[i].key=i; } sort(num+1,num+N+1); int ans=MIN; for(int i=1;i<=N;i++){ int Max=que(num[i].key);//找到编号小于num[i].key的最长上升子序列大小 update(num[i].key,++Max);//长度+1,然后更新后面,因为num有序,所以保证能够接在后面 } for(int i=N;i>0;i-=lowbit(i)) ans=max(ans,dp[i]); cout<<ans<<endl; return 0; }