hdu 5748(求解最长上升子序列的两种O(nlogn)姿势)
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5748
树状数组:
/* 对于普通的LIS: for(i):1~n LIS[i]=1; if j<i and a[j]<a[i] LIS[i]=LIS[j]+1 因此可知LIS转移需要两个条件 1.(j<i) 序号必须在i之前 2.(a[i]>a[j]) 值必须比a[i]小 利用树状数组的顺序操作:{查找的都是已经出现的,序号在前(满足条件1)} 对于每一个值,查找它在数组中的排名,再去寻找小于它的排名的最大的LIS(满足条件2) 这里利用到了排名,因为这样可以最大限度地压缩C数组的空间 */ #include <bits/stdc++.h> using namespace std; const int Max=1e5+10; int A[Max],V[Max],L[Max],C[Max],len; int lowbit(int x) {return x&(-x);} int Sum(int x) //求值小于等于x的LIS的最大值 { int ret=0; while(x>0) { if(C[x]>ret) ret=C[x]; x-=lowbit(x); } return ret; } void Add(int x,int d) //值大于等于x的LIS都改为LIS(x) { while(x<=len) { if(d>C[x]) C[x]=d; x+=lowbit(x); } } int main() { int T; for(scanf("%d",&T);T;T--) { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&A[i]); V[i]=A[i]; } sort(V+1,V+1+n); len=unique(V+1,V+1+n)-(V+1); memset(C,0,sizeof(C)); int ans=1,tmp,xu; for(int i=1;i<=n;i++) { xu=lower_bound(V+1,V+1+len,A[i])-(V); tmp=Sum(xu-1)+1; L[i]=tmp; Add(xu,tmp); } for(int i=1;i<=n;i++) { if(i!=1) printf(" "); printf("%d",L[i]); } puts(""); } return 0; }
dp+二分
/* 以dp[x]代表长度为x的LIS,且dp[x]==LIS长度为x的末尾值 每次都往前取dp[x]中最小的一个,当然在保证x尽可能地大的情况下 因为dp[x]是递增的,所以可以二分,l=1,r=当前最长的LIS 求得当前以小于当前a[i]的最长LIS */ #include <bits/stdc++.h> using namespace std; const int Max=1e5+10; int A[Max]; int dp[Max]; int LIS[Max]; void Get_lis(int n) { int i,j,l,r,mid,ans; dp[1]=A[1]; int len=1; for(i=2;i<=n;i++) { if(dp[len]<A[i]) j=++len; else { l=1;r=len; ans=0; while(l<=r) { mid=(l+r)>>1; if(A[i]>dp[mid]&&A[i]<=dp[mid+1]) { ans=mid;break; } else if(A[i]>dp[mid]) l=mid+1; else r=mid-1; } j=ans+1; } dp[j]=A[i]; LIS[i]=j; } } int main() { int T; for(scanf("%d",&T);T;T--) { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&A[i]); dp[i]=0; } LIS[1]=1; Get_lis(n); for(int i=1;i<=n;i++) { if(i!=1) printf(" "); printf("%d",LIS[i]); } puts(""); } return 0; }
其实还有一种单调队列求最长上升子序列的方法,可是不能用来解这道题
/* 无解。。。 单调队列只能求出总体的LIS长度 */ #include <bits/stdc++.h> using namespace std; const int Max=1e5+10; int que[Max]; int main() { int T; for(scanf("%d",&T);T;T--) { int n,x,top=0; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d",&x); if(x>que[top]||top==0) { que[++top]=x; } else { int l=1,r=top,mid,ans; ans=0; while(l<=r) { mid=l+(r-l)/2; if(que[mid]<x) l=mid+1; else r=mid-1,ans=mid; } que[ans]=x; } } cout<<top<<endl; } return 0; }