1996:登山

总时间限制: 5000ms 内存限制: 131072kB
描述
五一到了,PKU-ACM队组织大家去登山观光,队员们发现山上一个有N个景点,并且决定按照顺序来浏览这些景点,即每次所浏览景点的编号都要大于前一个浏览景点的编号。同时队员们还有另一个登山习惯,就是不连续浏览海拔相同的两个景点,并且一旦开始下山,就不再向上走了。队员们希望在满足上面条件的同时,尽可能多的浏览景点,你能帮他们找出最多可能浏览的景点数么?

输入
Line 1: N (2 <= N <= 1000) 景点数
Line 2: N个整数,每个景点的海拔
输出
最多能浏览的景点数
样例输入
8
186 186 150 200 160 130 197 220
样例输出
4
来源
第六届北京大学程序设计大赛暨ACM/ICPC选拔赛
本题刚开始写的时候应为就是最长上升子序列的方法写的只得了两分,还要注意到题目中还有下山的意思。坑点!附上ac代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,a[1001],p[1001],x[1001],i,maxi=-99999,j;
    cin>>n;
    for(i=1;i<=n;i++)
      cin>>a[i],p[i]=1,x[i]=1;
      for(i=2;i<=n;i++)
         for(j=1;j<i;j++)
           if(a[i]>a[j])
            p[i]=max(p[j]+1,p[i]);
            for(i=n ;i>=1 ;i--)
         for(j=n ;j>i;j--)
         if(a[i]>a[j]&&x[i]<x[j]+1) x[i]=x[j]+1;
      for(i=1;i<=n;i++)
        if(x[i]+p[i]>maxi)
          maxi=p[i]+x[i];
          cout<<maxi-1;
      return 0;
}
<think>好的,我现在需要解决用户关于登山问题的算法实现,具体是使用C++中的动态规划方法,实现最长非连续递增子序列,并且高度不能重复。首先,我需要理解登山问题的具体要求。 根据用户提到的引用内容,尤其是引用3,登山问题可能涉及原路返回和非原路返回的情况。题目可能要求找到一条路径,使得登山时高度递增,下山时高度递减,且不能重复经过相同高度的点。或者,可能类似于最长上升子序列的变种,需要同时考虑从左到右和从右到左的序列。 用户提到动态规划中的最长上升子序列模型,如引用2和引用4,所以可能需要将问题分解为两个方向的最长递增子序列。例如,对于每个位置i,计算左边的最长递增子序列长度和右边的最长递减子序列长度,然后两者相加减一(因为i点被计算了两次),取最大值作为结果。 接下来,我需要确保处理的是非连续的子序列,即允许不连续的元素,但保持递增或递减的顺序。同时,高度不能重复,这意味着序列中的每个元素必须严格递增或递减。 可能的步骤: 1. 定义两个数组,left和right。left[i]表示以i结尾的最长递增子序列长度,right[i]表示以i开头的最长递减子序列长度。 2. 从左到右遍历,填充left数组。对于每个i,检查之前的元素j,如果height[j] < height[i],则left[i] = max(left[i], left[j]+1)。 3. 从右到左遍历,填充right数组。类似地,对于每个i,检查后面的元素j,如果height[j] < height[i],则right[i] = max(right[i], right[j]+1)。 4. 最后,遍历每个点i,计算left[i] + right[i] - 1,取最大值即为所求的最长路径。 需要注意的问题: - 严格递增递减:条件应为height[j] < height[i],而不是≤,避免重复高度。 - 初始化时,每个left和right的初始值应为1,因为每个点自身构成长度为1的序列。 - 时间复杂度为O(n²),对于较大的n可能不够高效,但根据题目约束可能需要优化,不过用户没有提到具体数据规模,暂时按标准方法处理。 现在,我需要根据这些思路编写C++代码。首先定义两个数组,然后填充它们,最后计算结果。需要处理输入输出,假设输入是高度数组,输出是最长路径长度。 测试案例:比如输入是[1, 2, 3, 4, 5],输出应为5(递增到底,无法下山)。或者输入[100, 1, 2, 3],根据引用1的例子,最长递增子序列和为100,但最长长度是3,这里可能需要结合左右两部分。 另外,引用3中提到原路返回的问题,可能需要考虑登山和下山的路径是否重复。但用户的问题描述中强调不重复高度,可能意味着每个高度只能访问一次,所以路径必须是严格先递增后递减,且每个高度出现一次。因此,在动态规划过程中,每个位置的左右序列必须严格递增和递减,不会有重复。 编写代码时,需要注意数组索引是否正确,循环的方向是否正确。例如,right数组需要从右往左遍历,每个i从n到1,然后j从i+1到n,比较height[j] < height[i]时更新right[i]。 最后,验证代码的正确性,比如示例输入输出是否符合预期。</think>### C++实现登山问题的动态规划算法 登山问题要求找到一条先严格递增后严格递减的路径,且每个高度只能访问一次。这可以通过动态规划结合最长上升子序列模型解决。 #### 算法思路 1. **定义两个数组**: - `left[i]`:表示以第`i`个位置结尾的最长递增子序列长度。 - `right[i]`:表示以第`i`个位置开头的最长递减子序列长度。 2. **状态转移**: - **递增方向**:对于每个`i`,遍历`j < i`,若`height[j] < height[i]`,则更新`left[i] = max(left[i], left[j] + 1)`。 - **递减方向**:对于每个`i`,遍历`j > i`,若`height[j] < height[i]`,则更新`right[i] = max(right[i], right[j] + 1)`。 3. **合并结果**:每个位置`i`的最长路径为`left[i] + right[i] - 1`(减去重复计算的`i`点)。 #### C++代码实现 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { int n; cin >> n; vector<int> height(n); for (int i = 0; i < n; i++) cin >> height[i]; vector<int> left(n, 1), right(n, 1); // 计算递增序列(左到右) for (int i = 0; i < n; i++) { for (int j = 0; j < i; j++) { if (height[j] < height[i]) { left[i] = max(left[i], left[j] + 1); } } } // 计算递减序列(右到左) for (int i = n - 1; i >= 0; i--) { for (int j = n - 1; j > i; j--) { if (height[j] < height[i]) { right[i] = max(right[i], right[j] + 1); } } } int max_len = 0; for (int i = 0; i < n; i++) { max_len = max(max_len, left[i] + right[i] - 1); } cout << max_len << endl; return 0; } ``` #### 示例分析 - **输入**:`[1, 3, 5, 4, 2]` - **递增序列**:`left = [1, 2, 3, 3, 3]` - **递减序列**:`right = [3, 3, 2, 2, 1]` - **合并结果**:最大值在位置2(值为5),`left[2] + right[2] - 1 = 3 + 2 - 1 = 4`,路径为`1→3→5→4→2`(实际需验证是否符合严格递增递减)。 #### 关键点说明 - **严格递增递减**:通过条件`height[j] < height[i]`保证。 - **不重复高度**:动态规划过程中每个元素仅被计算一次,路径自然不重复。 - **时间复杂度**:$O(n^2)$,适用于中等规模数据[^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值