1. 程式人生 > >NYOJ 214.單調遞增子序列(二)(動態規劃)

NYOJ 214.單調遞增子序列(二)(動態規劃)

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

*/

思路:這道題是求一個數列中,最長的單調子序列。

首先,可以定義兩個陣列a[100010](儲存原數列),dp[100010](儲存最長子序列)。

然後,dp陣列中的元素與a陣列中的某個元素進行比較,如果這個元素比dp陣列最後一個元素大,將這個元素儲存到dp陣列中。(這一部分可以用二分查詢,找到比該元素大於或等於的元素的位置)

最後,輸出dp陣列的個數。

下面是程式碼實現:

#include<stdio.h>
#include<string.h>
int main()
{
	int n;
	int a[100005],dp[100005],count;
	while(~scanf("%d",&n))
	{
		count=0;
		memset(dp,0x3f,sizeof(dp));
		int i;
//		memset(dp,0,sizeof(dp));
//		int i,j; 
		for(i=0;i<n;i++)
		    scanf("%d",&a[i]);
 		for(i=0;i<n;i++)//二分查詢,找到大於或等於該元素的第一個位置 
		{
			int low=0,high=count,mid;
		    while(low<=high)
		    {
			    mid=(low+high)/2;
			    if(dp[mid]>=a[i])
				    high=mid-1;					
			    else
				    low=mid+1;
		    }
			dp[low]=a[i];//將該元素儲存的查詢到的位置	
		    if(low==count)//若該位置是原dp陣列的最後一位,count加1 
		    	count++;
//			for(j=count;j>=0;j--)//從dp陣列的最後一位開始進行比較 
//			{                    // (這種方法用於資料量少的,用於資料量大的將有可能超時,需要用到二分查詢)
//				if(a[i]>dp[j])   //若成立,儲存到dp陣列中 
//				{
//				    dp[j+1]=a[i];
//				    if(j==count)
//					    count++;
//					break;				    
//				}				
//			}			    			    
		}
		printf("%d\n",count);
	}
	return 0;
}

另外,還可以用lower_bound()函式進行查詢(其實上面程式中的二分查詢就相當於該函式)

下面是程式碼實現:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int n;
	int a[100005],dp[100005];
	while(~scanf("%d",&n))
	{
		fill(dp,dp+n,0x3f3f3f3f);
		for(int i=0;i<n;i++)
			scanf("%d",&a[i]);			
		for(int i=0;i<n;i++)
			*(lower_bound(dp,dp+n,a[i]))=a[i];				
		printf("%d\n",lower_bound(dp,dp+n,0x3f3f3f3f)-dp);
	}
	return 0;
}