1. 程式人生 > 其它 >acwing 1014. 登山| 最長“上升再下降”的子序列

acwing 1014. 登山| 最長“上升再下降”的子序列

目錄

題目傳送門

題目描述

五一到了,ACM隊組織大家去登山觀光,隊員們發現山上一共有N個景點,並且決定按照順序來瀏覽這些景點,即每次所瀏覽景點的編號都要大於前一個瀏覽景點的編號。

同時隊員們還有另一個登山習慣,就是不連續瀏覽海拔相同的兩個景點,並且一旦開始下山,就不再向上走了。

隊員們希望在滿足上面條件的同時,儘可能多的瀏覽景點,你能幫他們找出最多可能瀏覽的景點數麼?

輸入格式

第一行包含整數N,表示景點數量。

第二行包含N個整數,表示每個景點的海拔。

輸出格式

輸出一個整數,表示最多能瀏覽的景點數。

資料範圍

2≤N≤10002≤N≤1000

輸入樣例:

8
186 186 150 200 160 130 197 220

輸出樣例:

4

最長上升子序列

分析

這個題目的題意不是很好理解

其實就是讓求得:先上升,後下降的最長子序列的長度

所以我們分兩個部分來求,對於每個點a[i]

  • 首先求該點到左端點的最長下降子序列的長度f[i]:也就是從左端點到a[i]的最長上升子序列的長度
  • 然後求該點到右端點的最長下降子序列的長度g[i]也就是從右端點到a[i]的最長上升子序列長度
    • 注意求g[i]的時候需要逆序求,它不等於 左端點到該點的最長上升子序列的長度

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int a[N]; 
int g[N], f[N]; 
int k, n;
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
		
	for(int i = 1; i <= n; i++)
	{
		f[i] = 1;
		for(int j = 1; j < i; j++)
		{
			if(a[j] < a[i]) f[i] = max(f[i], f[j] + 1); // 從左端點開始到a[i]的最長上升序列長度 
		}
	}
	
	for(int i = n; i >= 1; i--)
	{
	    g[i] = 1;
		for(int j = n; j > i; j--)
		{
			if(a[i] > a[j]) g[i] = max(g[i], g[j] + 1); // 從a[i]開始到右端點的最長下降序列長度
														// 也就是從右端點到a[i]的最長上升序列程度 
		}
	}
	
	// 注意上面的g[i]不能寫成這個形式:
	// 因為這個的g[i]表示的含義是從左邊端點到a[i]的最長下降子序列長度
	// 並不是我們要的"a[i]開始到右端點的最長下降序列長度" 
	/*for(int i = 1; i <= n; i ++){
        g[i] = 1;
        for(int j = 1; j < i; j ++){
            if(a[i] > a[j]) g[i] = max(g[i], g[j] + 1);
        }
    }*/ 
 
		
	
	int res = 0;
	for(int i = 1; i <= n; i++)
	{
		res = max(res, f[i] + g[i] - 1);
	}
	printf("%d\n", res);

	return 0;
}

時間複雜度

參考文章

https://www.acwing.com/problem/content/discussion/content/3331/