最大上升子序列,最大下降子序列,最大非增子序列,最大非減子序列


For example,{1,5,2,4,3,5,6,4,7}的最大上升子序列是{1,2,3,5,6,7}長度為6

現已知原序列a[],如何求其最大上升子序列,最大下降子序列,最大非增子序列,最大非減子序列的長度?
下面貼出兩種方法:1.dp, 2.貪心+二分

#include<cstdio>
#include<cstring>
#include<iostream> 
#include<algorithm>
using namespace std;

#define maxn 1000
const int inf=1<<30;
int a[maxn];//原序列 


int dp[maxn];//dp[i]代表以a[i]結尾的最大非增子序列 
int lnip1(int n) //動態規划,時間復雜度O(n2) 
{
	for(int i=0;i<n;i++)
	dp[i]=1;
	for(int i=1;i<n;i++)
	{
		for(int j=0;j<i;j++)
		{
			if(a[j]>=a[i])//最大非增子序列,若改為a[j]>a[i],即為最大遞減子序列 
			{
				dp[i]=max(dp[j]+1,dp[i]);
			}
		}
	}
	int ans=0;
	for(int i=0;i<n;i++)
	{
		ans=max(ans,dp[i]);
	}
	return ans;
	
 } 



 
 int b[maxn];//b[k] 記錄前i個數中的長度為k的所有子序列中最后一位最小的值,b數組一定有序 
 int lndp(int n)//最大非遞減子序列長度,時間復雜度O(nlogn) 
 {
 	for(int i=0;i<=n+1;i++)
 	b[i]=inf;
 	for(int i=0;i<n;i++)
 	{
 	    int pos=lower_bound(b,b+i+1,a[i])-b;//如果是嚴格遞增(lip)用upper_bound 
 		b[pos]=a[i];
	 }
	 int ans;
	 for(ans=0;b[ans]!=inf;ans++);
	 return ans;
	//如果題目要求求最大非遞增子序列(lnip)長度,只需先把數組反過來,再求lndp即可,最大遞減類似 
 }
 
 int main()
 {
 	int n;
 	cin>>n;
 	for(int i=0;i<n;i++)
 	cin>>a[i];
 	cout<<lnip1(n)<<endl;
 	cout<<lndp(n);
 }

第一種方法比較簡單,不做過多詳解_

下面詳解第二種貪心+二分,時間復雜度位O(nlongn)的算法(注意:下面以嚴格上升子序列為例,請把上面代碼中的lower_bound換成upper_bound):

首先,

b[maxn] b[k] 記錄前i個數中的長度為k+1的所有上升子序列中最后一位最小的值
記錄下最后一位最小的值,是為了能有更大的上升空間
比如序列{1,3,2,3,4}
前三個數{1,3,2}中長度為2的上升子序列有{1,3}和{1,2},分別對應的b[1](長度為1+1=2的上升子序列的最后一位的值)為3,2,明顯,選2這個較小的可以給后面的序列留下更多的上升空間,選2可以是{1,2,3,4}而選3只能是{1,3,4},b[k]的值盡可能小,
這就是其貪心的思想
b數組一定是嚴格上升的,因為長度為i的最后一位最優不可能比長度為i-1的最后一位小。
既然b數組是有序的,接下來就能用二分的方法了
先將b的元素最大化,然后將將原序列a的元素一個一個插入到b中合適的位置--b中第一個大於a[i]的位置,這樣如果a[i]能夠大於b[k]說明a[i]能夠放在b[k](其實是b[k]的值對應的原序列元素)的后面,而到了第一個大於a[i]的位置,
假設那個位置x上的的值為y(y可以為inf),a[x]=y>a[i],再根據上面貪心的思想,何不把結尾換為更小的值a[i]呢,即b[x]=a[i];
這樣插入完所有的a[i](其實就是將a[i]插在能以它為結尾的最大子序列長度的位置上),一重循環到最后一個b[i]!=inf,然后這個i+1就是最大上升子序列的長度

 接下來再以{1,5,2,4,3,5,6,4,7}為例,畫個圖讓大家理解理解:

 (字有點差,勿噴)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM