AcWing 1014 登山

爱⌒轻易说出口 提交于 2020-01-20 12:31:58

题目描述:

五一到了,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;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!