單調遞增子序列(二)
描述
給定一整型數列{a1,a2...,an}(0<n<=100000),找出單調遞增最長子序列,並求出其長度。
如:1 9 10 5 11 2 13的最長單調遞增子序列是1 9 10 11 13,長度爲5。
輸入
有多組測試數據(<=7)
每組測試數據的第一行是一個整數n表示序列中共有n個整數,隨後的下一行裏有n個整數,表示數列中的所有元素.每個整形數中間用空格間隔開(0<n<=100000)。
數據以EOF結束 。
輸入數據保證合法(全爲int型整數)!
輸出
對於每組測試數據輸出整形數列的最長遞增子序列的長度,每個輸出佔一行。
樣例輸入
7
1 9 10 5 11 2 13
2
2 -1
樣例輸出
5
1
來源
這個題和前面做的那個單調遞增子序列題目意思是一樣的,但是這個題目數據量比較大,用前面的那種方法就會超時,前面的那一種方法dp[i]:用來儲存以ai爲末尾的最長遞增子序列的長度;時間複雜度是o(n2),這個題目的數據範圍到了100000,用這種方法就會超時;
所以我們採用另一種方法,用dp[i]:儲存長度爲i+1的遞增序列中末尾元素的最小值;
建立一個一維數組dp[ ],用dp[i]保存長度爲i的單調子序列的末尾元素的值,用top保存單調子序列的最大長度。
初始top=1,dp[1]=a1;
然後,我們從前向後遍歷序列An(i : 2~n)。顯然,我們會遇到兩種情況:
1.a[i] > dp[top]。此時,我們把a[i]加入到長度爲top的單調序列中,這時序列長度爲top+1,top值也跟着更新,即dp[++top] = a[i];
2.a[i]<=dp[top]。此時,a[i]不能加入長度爲top的單調序列,那它應該加入長度爲多少的序列呢?
做法是,從後向前遍歷dp[ ] (j: top-1~1),直到滿足條件 a[i] > dp[j],此時,類似於步驟1,我們可以把a[i]加入到長度爲j的單調序列中,這時此序列長度爲j+1;(這段話轉載於http://www.cnblogs.com/mycapple/archive/2012/08/22/2651453.html)
如果直接這樣做的話,時間複雜度也會達到o(n2),但是這裏還可以優化,我們在遍歷更新的時候可以採用更高效率的方法;這裏我們可以採用二分搜索,對前面的a[i]進行更新;這種方法的時間複雜度就是o(nlogn)
下面是代碼:
#include <cstdio>
#include <cstring>
const int maxn=100001;
int dp[maxn],a[maxn];
int Binary_search(int len,int k)//二分搜索
{//查找比第一個比dp[i]小或者是相等的位置
int start,end,mid;
start=1;
end=len;
while(start<=end)
{
mid=(start+end)>>1;
if(k==dp[mid])
return mid;
if(k>dp[mid])
start=mid+1;
else
end=mid-1;
}
return start;
}
int main()
{
int n,i,t,len;
while(scanf("%d",&n)!=EOF)
{
memset(dp,0,sizeof(dp));
for(i=0;i<n;i++)
scanf("%d",&a[i]);
len=1;
dp[1]=a[0];//這裏要從1開始
for(i=1;i<n;i++)
{
t=Binary_search(len,a[i]);//通過二分搜索查找a[i]要插入的位置
dp[t]=a[i];//更新dp的值
if(t>len)//如果插入的位置在最後,更新最大的長度
len=t;
}
printf("%d\n",len);
}
}
下面是最優代碼,好簡短的:用了一個STL裏面的lower_bound函數,這個函數就是二分查找,返回第一個比val小的位置,把這個函數使用熟練也會比較方便,與此相對的還有一個upper_bound函數;http://blog.csdn.net/niushuai666/article/details/6734403 這個內容可以參考這篇博客
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX=100100;
int num[MAX],top=0;
int main()
{
int n;
while(~scanf("%d",&n))
{
scanf("%d",&num[0]);
top=1;
for(int i=1;i!=n;i++)
{
scanf("%d",&num[i]);
int * p=lower_bound(num,num+top,num[i]);
if(p-num==top) ++top;
*p=num[i];
}
printf("%d\n",top);
}
}