nyist oj 214 單調遞增子序列(二) (動態規劃經典)

單調遞增子序列(二)

時間限制:1000 ms  |  內存限制:65535 KB
難度:4
 

描述

給定一整型數列{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

來源

[521521]改編

這個題和前面做的那個單調遞增子序列題目意思是一樣的,但是這個題目數據量比較大,用前面的那種方法就會超時,前面的那一種方法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);
	}
	
}        

 

 

 


 

發佈了219 篇原創文章 · 獲贊 365 · 訪問量 68萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章