题目描述:
五一到了,ACM队组织大家去登山观光,队员们发现山上一个有N个景点,并且决定按照顺序来浏览这些景点,即每次所浏览景点的编号都要大于前一个浏览景点的编号。
同时队员们还有另一个登山习惯,就是不连续浏览海拔相同的两个景点,并且一旦开始下山,就不再向上走了。
队员们希望在满足上面条件的同时,尽可能多的浏览景点,你能帮他们找出最多可能浏览的景点数么?
输入格式
第一行包含整数N,表示景点数量。
第二行包含N个整数,表示每个景点的海拔。
输出格式
输出一个整数,表示最多能浏览的景点数。
数据范围
2≤N≤1000
输入样例:
8
186 186 150 200 160 130 197 220
输出样例:
4
分析:
本题题意是按顺序浏览景点,不连续浏览两个海拔相同的景点。且一旦下山就不再上山了,这意味着访问景点的顺序是个单峰序列,即先向高处景点行走,之后再向低处景点行走。问题的关键在于这个峰值是哪点,设dp[i]表示以i点为峰值的单峰子序列的最大值,即dp[i]等于以i为末尾的最长上升子序列的长度加上以i为开头的最长下降子序列的长度。如果用一重循环取枚举峰值点i,然后再用两层循环取求以i点为分界线的最长上升子序列长度和最长下降子序列长度,时间复杂度将是立方级别的。不妨预处理下以h[i]为末尾的LIS长度以及以h[i]为开头的最长下降子序列的长度。如果我们g[i]表示以h[i]为结尾的最长下降子序列长度,那么我们最终要使用的是以h[i]为开头的最长下降子序列长度,不太方便,故设g[i]为以h[i]为开头的最长下降子序列长度,在怪盗基德的滑翔翼问题中,我们逆向的求LIS长度求得的实际就是以h[i]为开头的最长下降子序列长度,故只需要逆向遍历下h[i],求得的就是以h[i]为开头的最长下降子序列长度。
总结下就是f[i]表示以h[i]为末尾的LIS长度,g[i]表示以h[i]为开头的最长下降子序列长度。f[i] = max(f[i],f[j] + 1),j < i且h[i] > h[j];g[i] = max(g[i],g[j] + 1),j > i且h[i] > h[j]。这里要注意的是不论是正向求LIS,还是逆向求LIS都是h[i] > h[j],因为方向本题,i与j的大小关系也不同。最后dp[i] = f[i] + g[i] - 1。因为峰值点h[i]被重复统计了,所以需要减一。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
int h[N];
int f[N],g[N];
int main()
{
int n;
cin >> n;
int res = 0;
for(int i = 1; i <= n; i++) cin>>h[i];
for(int i = 1;i <= n;i++){
f[i] = 1;
for(int j = 1;j < i;j++){
if(h[i] > h[j]) f[i] = max(f[i],f[j] + 1);
}
}
for(int i = n;i >= 1;i--){
g[i] = 1;
for(int j = n;j > i;j--){
if(h[i] > h[j]) g[i] = max(g[i],g[j] + 1);
}
}
for(int i = 1;i <= n;i++) res = max(res,f[i] + g[i] - 1);
cout << res << endl;
return 0;
}
来源:CSDN
作者:昂昂累世士
链接:https://blog.csdn.net/qq_30277239/article/details/104049300