#P3191. 攀登者2(200分)
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 110
            Accepted: 29
            Difficulty: 4
            
          
          
          
                       所属公司 : 
                              华为od
                                
            
                      
          
 
- 
                        算法标签>模拟          
 
攀登者2(200分)
题面描述
攀登者喜欢寻找各种地图,并尝试攀登到最高的山峰。地图表示为一维数组,数组的索引代表水平位置,数组的元素代表相对海拔高度。其中数组元素 0 代表地面。
例如:[0,1,2,4,3,1,0,0,1,2,3,1,2,1,0],代表如下图所示的地图,地图中有两个山脉位置分别为 1,2,3,4,5 和 8,9,10,11,12,13,最高峰高度分别为 4,3。最高峰位置分别为 3,10。
一个山脉可能有多座山峰(高度大于相邻位置的高度,或在地图边界且高度大于相邻的高度)。
登山时会消耗登山者的体力(整数),
- 上山时,消耗相邻高度差两倍的体力
 - 下山时,消耗相邻高度差一倍的体力
 - 平地不消耗体力
 
登山者体力消耗到零时会有生命危险。
攀登者想要评估一张地图内有多少座山峰可以进行攀登,且可以安全返回到地面,且无生命危险。
思路
这道题的核心思路是通过遍历地图数组识别所有山峰,然后分别计算从地面到每个山峰的最小体力消耗以及从山峰返回地面的最小体力消耗,判断攀登者的体力是否足够完成整个攀登和返回过程,最终统计满足条件的山峰数量。注意:要到达某一座山峰的途中可能需要经过其它山峰。
- 山峰识别:首先需要识别地图中的所有山峰。山峰的定义是高度大于相邻位置的高度,或者在地图边界且高度大于相邻的高度。
 - 体力计算:对于每一个山峰,计算从地面到山峰的最小体力消耗,以及从山峰返回地面的最小体力消耗。
 - 体力判断:判断攀登者的体力是否足够完成整个攀登和返回的过程。
 - 统计结果:统计满足条件的山峰数量。
 
cpp
#include <iostream>
#include <vector>
#include <sstream>
#include <climits>  
 
using namespace std;
 
// 计算从起点到终点的体力消耗
int calculate_energy(const vector<int>& map_data, int start, int end) {  
    int energy = 0;  
    if (start < 0 || start >= (int)map_data.size())  return INT_MAX; // 位置不合法 
    if (end < 0 || end >= (int)map_data.size())  return INT_MAX;      
    
    if (start < end) {
        for (int i = start; i < end; ++i) {
            int diff = map_data[i+1] - map_data[i];
            energy += (diff > 0) ? 2 * diff : -diff;
        }
    } else {
        for (int i = start; i > end; --i) {
            int diff = map_data[i-1] - map_data[i];
            energy += (diff > 0) ? 2 * diff : -diff;
        }
    }
    return energy;
}
 
int main() {
    // 读取地图数据 
    string map_str;
    getline(cin, map_str);
    stringstream ss(map_str);
    vector<int> map_data;
    int num;
    while (ss >> num) {
        map_data.push_back(num); 
        if (ss.peek()  == ',') ss.ignore(); 
    }
 
    // 读取体力值 
    long long energy;
    cin >> energy;
 
    // 识别山峰 
    vector<int> peaks;
    for (int i = 0; i < (int)map_data.size();  ++i) {
        bool left = (i == 0) || (map_data[i] > map_data[i-1]);
        bool right = (i == (int)map_data.size()-1)  || (map_data[i] > map_data[i+1]);
        if (left && right) peaks.push_back(i); 
    }
    
    // 统计满足条件的山峰数量
    int count = 0;
    for (int peak : peaks) {
        // 寻找左右地面 
        int left = peak - 1;
        while (left >= 0 && map_data[left] != 0) --left;
        int right = peak + 1;
        while (right < (int)map_data.size()  && map_data[right] != 0) ++right;
 
        
        // 计算上山和下山是最小体力消耗
        int up = min(calculate_energy(map_data, left, peak),
                    calculate_energy(map_data, right, peak));
        int down = min(calculate_energy(map_data, peak, right),
                      calculate_energy(map_data, peak, left));
        
        long long total = 0ll + up + down;  
        if (total < energy) ++count;
    }
 
    cout << count << endl;
    return 0;
}
python
def calculate_energy(map_data, start, end):
    energy = 0
    if start < 0 or start >= len(map_data):
        return float('inf')
    if end < 0 or end >= len(map_data):
        return float('inf')
    if start < end:
        for i in range(start, end):
            diff = map_data[i + 1] - map_data[i]
            if diff > 0:
                energy += 2 * diff
            elif diff < 0:
                energy += -diff
    else:
        for i in range(start, end, -1):
            diff = map_data[i - 1] - map_data[i]
            if diff > 0:
                energy += 2 * diff
            elif diff < 0:
                energy += -diff
    return energy
def main():
    # 输入地图
    map_data = list(map(int, input().split(',')))
    
    # 输入体力
    energy = int(input())
    
    # 识别所有山峰
    peaks = []
    for i in range(len(map_data)):
        if (i == 0 or map_data[i] > map_data[i - 1]) and (i == len(map_data) - 1 or map_data[i] > map_data[i + 1]):
            peaks.append(i)
    
    # 统计满足条件的山峰数量
    count = 0
    for peak in peaks:
        # 找到最近的左右地面
        left = peak - 1
        while left >= 0 and map_data[left] != 0:
            left -= 1
        right = peak + 1
        while right < len(map_data) and map_data[right] != 0:
            right += 1
        
        # 计算上山和下山最小体力消耗
        up = min(calculate_energy(map_data, left, peak), calculate_energy(map_data, right, peak))
        down = min(calculate_energy(map_data, peak, right),calculate_energy(map_data, peak, left))
        total_energy = up + down
        # 判断体力是否足够
        if total_energy < energy:
            count += 1
    
    # 输出结果
    print(count)
if __name__ == "__main__":
    main()
java
import java.util.*; 
 
public class Main {
    public static int calculateEnergy(List<Integer> mapData, int start, int end) {
        int energy = 0;
        if (start < 0 || start >= mapData.size())  return Integer.MAX_VALUE;
        if (end < 0 || end >= mapData.size())  return Integer.MAX_VALUE;
        
        if (start < end) {
            for (int i = start; i < end; i++) {
                int diff = mapData.get(i+1)  - mapData.get(i); 
                energy += (diff > 0) ? 2 * diff : -diff;
            }
        } else {
            for (int i = start; i > end; i--) {
                int diff = mapData.get(i-1)  - mapData.get(i); 
                energy += (diff > 0) ? 2 * diff : -diff;
            }
        }
        return energy;
    }
 
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in); 
        
        // 读取地图数据 
        String[] mapStr = sc.nextLine().split(","); 
        List<Integer> mapData = new ArrayList<>();
        for (String s : mapStr) {
            mapData.add(Integer.parseInt(s.trim())); 
        }
        
        // 读取体力值 
        long energy = sc.nextLong(); 
        
        // 识别山峰 
        List<Integer> peaks = new ArrayList<>();
        for (int i = 0; i < mapData.size();  i++) {
            boolean left = (i == 0) || (mapData.get(i)  > mapData.get(i-1)); 
            boolean right = (i == mapData.size()-1)  || (mapData.get(i)  > mapData.get(i+1)); 
            if (left && right) peaks.add(i); 
        }
        
        int count = 0;
        for (int peak : peaks) {
            // 寻找左右地面 
            int left = peak - 1;
            while (left >= 0 && mapData.get(left)  != 0) left--;
            int right = peak + 1;
            while (right < mapData.size()  && mapData.get(right)  != 0) right++;
            
            // 计算上下山能量 
            int up = Math.min( 
                calculateEnergy(mapData, left, peak),
                calculateEnergy(mapData, right, peak)
            );
            int down = Math.min( 
                calculateEnergy(mapData, peak, right),
                calculateEnergy(mapData, peak, left)
            );
            long total = 0L + up + down;
            
            if (total < energy) count++;
        }
        
        System.out.println(count); 
    }
}
        题目内容
攀登者喜欢寻找各种地图,并且尝试攀登到最高的山峰。
地图表示为一维数组,数组的索引代表水平位置,数组的元素代表相对海拔高度。其中数组元素 0 代表地面。
例如:[0,1,2,4,3,1,0,0,1,2,3,1,2,1,0],代表如下图所示的地图,地图中有两个山脉位置分别为 1,2,3,4,5 和 8,9,10,11,12,13,最高峰高度分别为 4,3。最高峰位置分别为 3,10。
一个山脉可能有多座山峰(高度大于相邻位置的高度,或在地图边界且高度大于相邻的高度)。

登山时会消耗登山者的体力(整数),
- 
上山时,消耗相邻高度差两倍的体力
 - 
下山时,消耗相邻高度差一倍的体力
 - 
平地不消耗体力
 
登山者体力消耗到零时会有生命危险。
例如,上图所示的山峰:
- 
从索引 0 ,走到索引 1 ,高度差为 1 ,需要消耗 2∗1=2 的体力,
 - 
从索引 2 ,走到索引 3 ,高度差为 2 ,需要消耗 2∗2=4 的体力。
 - 
从索引 3 ,走到索引 4 ,高度差为 1 ,需要消耗 1∗1=1 的体力。
 
攀登者想要评估一张地图内有多少座山峰可以进行攀登,且可以安全返回到地面,且无生命危险。
例如上图中的数组,有 3 个不同的山峰,登上位置在 3 的山可以从位置 0 或者位置 6 开始,从位置 0 登到山顶需要消耗体力 1∗2+1∗2+2∗2=8 ,从山顶返回到地面0需要消耗体力 2∗1+1∗1+1∗1=4 的体力,按照登山路线 0 → 3 → 0 需要消耗体力12 。攀登者至少需要 12 以上的体力(大于 12 )才能安全返回。
输入描述
第一行输入为地图一维数组
第二行输入为攀登者的体力
输出描述
确保可以安全返回地面,且无生命危险的情况下,地图中有多少山峰可以攀登。
样例1
输入
0,1,4,3,1,0,0,1,2,3,1,2,1,0
13
输出
3
说明
登山者只能登上位置 10 和 12 的山峰,7 → 10 → 7,14 → 12 → 14
样例2
输入
1,4,3
999
输出
0
说明
没有合适的起点和终点