Description
Input
Output
Sample Input
4 6 4 2 6 3 1 5 10 2 3 4 5 6 7 8 9 10 1 8 8 7 6 5 4 3 2 1 9 5 8 9 2 3 1 7 4 6
Sample Output
3 9 1 4
题目大意:求最长上升子序列,序列长度最大为40000。
分析:如果用一般的LIS算法,时间复杂度高达n^2。这里引用《入门经典》复杂度为O(nlogn)的方法。
假设已经计算出的两个状态 a 和 b 满足Aa < Bb 且d(a)==d(b),则对于后续所有状态 i(即i>a且i>b)来说,a并不会比b差——如果b满足Ab < Ai的条件,a也满足,且二者的d值相同;但反过来却不一定了。换句话说,如果我们只保留a,一定不会丢失最优解。
这样,对于相同的d值,只需要保留A最小的一个。我们用g(i)表示d值为i的最小状态编号。根据上述推理证明
g(1)<=g(2)<=g(3)<=...<=g(n)
上述的g值是动态改变的。对于一个给定的状态i,我们只考虑在i之前已经计算过的状态j(即j<i)。在给定状态i时可以用二分查找得到满足g(k)>=Ai的第一个下标k,则d(i)=k,此时Ai<g(k),而d(i)=k,所以更新g(k)=Ai。(话说看的不是很明白)
for(i=1; i<=n; i++) g[i] = INF;
for(i=0; i<n; i++)
{
int k = lower_bound(g+1,g+n+1,A[i]) - g;
d[i]=k;
g[k] = A[i];
}
代码如下:
1 # include<cstdio> 2 # include<iostream> 3 # include<algorithm> 4 using namespace std; 5 # define INF 0xffffff 6 int n; 7 int g[40005],A[40005]; 8 9 int main() 10 { 11 int i,T; 12 scanf("%d",&T); 13 while(T--) 14 { 15 scanf("%d",&n); 16 for(i=0; i<n; i++) 17 scanf("%d",&A[i]); 18 int ans = 0; 19 for(i=1; i<=n; i++) g[i] = INF; 20 for(i=0; i<n; i++) 21 { 22 int k = lower_bound(g+1,g+n+1,A[i]) - g; 23 g[k] = A[i]; 24 if(k>ans) 25 ans = k; 26 } 27 printf("%d\n",ans); 28 } 29 return 0; 30 }
LIS nlogn算法大罗列!
网上有这一方面的总结 //n是原序列长度,a[]是原序列,D是a[]的值域大小
1. f[i]表示a[i]结尾的LIS长度,f[i] = max{f[j]}+1 : a[j]<a[i]
1.1 维护一个以a的值为下标,以f的值为值的树状数组优化转移。O(n log D)
1.2 g[x]表示长度为x的所有LIS中最小的末尾的值,可证g[x]单调递增,二分查找转移。O(n log n)
1.3 维护一个“最优”的LIS q,每次将q关于a[i]的lower_bound更新为a[i],同时转移。O(n log n)
1.2代码如下:
1 #include <iostream> 2 using namespace std; 3 4 int a[40001]; 5 int dp[40001]; 6 int b[40001], blen; 7 int n; 8 9 int main() { 10 int ca,i; 11 scanf("%d", &ca); 12 while (ca--) { 13 scanf("%d", &n); 14 for (i = 1; i <= n; ++i) { 15 scanf("%d", a+i); 16 } 17 memset(b,0,sizeof(b)); 18 memset(dp,0,sizeof(dp)); 19 20 21 int left, right, mid; 22 blen = 0; 23 int res = 0; 24 for (i = 1; i <= n; ++i) { 25 left = 1; 26 right = blen; 27 int num = a[i]; 28 while (left <= right) { 29 mid = (left + right)/2; 30 if (b[mid] < a[i]) { 31 left = mid + 1; 32 } 33 else { 34 right = mid - 1; 35 } 36 } 37 dp[i] = left; 38 b[left] = a[i]; 39 if (blen < left) 40 blen = left; 41 if (res < dp[i]) 42 res = dp[i]; 43 } 44 printf("%d\n", res); 45 } 46 return 0; 47 }