(1)问题描述:给定n个整数A1,A2,A3...An。按照从左往右的顺序选择尽可能多的整数,组成一个上升子序列,其中相邻元素不能相等。
(2)解题思路:本题就是经典的最长上升子序列问题(Longest Increasing Subsequence,LIS)。可以通过动态规划解决。定义状态d(i)表示以下标i结尾的LIS的最大长度。那么不难得到如下状态转移方程:
d(i)=max{0,d(j)|j<i,Aj<Ai}+1;
最终的答案为max{d(i)}。时间复杂度为O(N^2)。由于这种方法十分常见,不予赘述。接下来介绍O(N*logN)的算法。
考虑这样一个事实,给定两个下标a,b(注意,这里a,b大小未知),如果它们满足Aa<Ab且d(a)==d(b)。则对于所有的i>max{a,b},a并不会比b差。另外,如果b满足Ab<Ai,那么对于a也满足这个性质,且二者的d值相同。但是,如果Aa<Ai,却不一定有Ab<Ai。
通过以上的事实,我们发现,对于某一个d’值,只要保留最小的那个Aa,使得d(a)==d‘即可。我们用g(i)表示d值为i的最小状态的编号(即i对应的最小的那个Aa,如果不存在设置为INF)。根据以上的推理有如下不等式:
g(1)≤g(2)≤g(3)≤...≤g(n)
注意,上述的g(i)是会动态改变的。对于一个给定的状态i,我们只考虑在i之前已经计算过的状态j(即j<i),上述的g值也是基于这些状态而改变的。随着i的增大,我们要考虑的状态也越来越多,g也随之发生改变。在给定状态i时,可以用二分查找得到满足g(k)≥Ai的第一个下标k(实际上是要找g(k‘)<Ai的最后一个下标k‘,则d(i)=k‘+1,令k=k‘+1即可得到,这里用lower_bound省略了k‘+1的操作),则d(i)=k,此时Ai<g(k).而d(i)=k,所以更新g(k)=Ai(此时正是利用了上述的事实!虽然这里的g(k)在变小,但仍然大于等于g(k-1),满足上述的不等式)。
typedef long long ll; typedef unsigned long long ull; #define me(s) memset(s,0,sizeof(s)) #define For(i,n) for(int i=0;i<(n);i++) #define N 100 #define INF 100000000 int a[N]; int g[N]; int d[N]; int main() { freopen("t.txt","r",stdin); int n; while(~scanf("%d",&n)) { me(a);me(g);me(d); fill(g+1,g+n+1,INF); For(i,n) scanf("%d",&a[i]); For(i,n) { int k=lower_bound(g+1,g+n+1,a[i])-g; d[i]=k; g[k]=a[i]; } printf("%d\n",d[n-1]); } return 0; }