bluecode-2023A-天然蓄水池

105 阅读4分钟

时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 128MB,其他语言 256MB
难度:困难

描述

公元 2919 年,人类终于发现了一颗宜居星球——X 星。现想在 X 星一片连绵起伏的山脉间建一个天热蓄水库,如何选取水库边界,使蓄水量最大? 

山脉用正整数数组 s 表示,每个元素代表山脉的高度。选取山脉上两个点作为蓄水库的边界,则边界内的区域可以蓄水,蓄水量需排除山脉占用的空间。

蓄水量的高度为两边界的最小值。 如果出现多个满足条件的边界,应选取距离最近的一组边界。 输出边界下标(从 0 开始)和最大蓄水量;如果无法蓄水,则返回 0,此时不返回边界。 

例如,当山脉为 s=[3,1,2]时,则选取 s[0]和 s[2]作为水库边界,最大蓄水量为 1,此时输出:0 2:1 

当山脉 s = [3,2,1]时,不存在合理的边界,此时输出 0。

输入描述

一行正整数,用空格隔开,例如输入1 2 3表示 s = [1,2,3]

输出描述

当存在合理的水库边界时,输出左边界、空格、右边界、英文冒号、蓄水量,例如0 2:1 

当不存在合理的水库边界时,输出 0。

用例输入 1 **

1 9 6 2 5 4 9 3 7

用例输出 1 **

1 6:19

提示

经过分析,选取 s[1]和 s[6] 时,水库蓄水量为 3+7+4+5 = 19 为最大蓄水量

双指针

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 主函数
int main() {
    // 输入山脉高度数组
    vector<int> s;
    int num;
    while (cin >> num) {
        s.push_back(num);
        if (cin.get() == '\n') break;  // 按行读取输入
    }

    int n = s.size();
    if (n < 3) {  // 至少需要三个点才能形成蓄水区域
        cout << "0" << endl;
        return 0;
    }

    int left = 0, right = n - 1;
    int max_water = 0;
    pair<int, int> result = {0, 0};  // 存储最优边界

    while (left < right) {
        // 当前边界的蓄水高度
        int height = min(s[left], s[right]);

        // 计算当前区域的蓄水量
        int water = height * (right - left - 1);
        for (int i = left + 1; i < right; ++i) {
            water -= s[i];  // 减去山脉占用的空间
        }

        // 更新最大蓄水量和边界
        if (water > max_water) {
            max_water = water;
            result = {left, right};
        }

        // 移动指针
        if (s[left] < s[right]) {
            left++;
        } else {
            right--;
        }
    }

    // 如果最大蓄水量为 0,则无法蓄水
    if (max_water == 0) {
        cout << "0" << endl;
    } else {
        // 输出结果
        cout << result.first << " " << result.second << ":" << max_water << endl;
    }

    return 0;
}
package main

import (
    "fmt"
)

func maxWaterStorage(s []int) string {
    n := len(s)
    if n < 3 { // 至少需要三个点才能形成蓄水区域
        return "0"
    }

    left, right := 0, n-1
    maxWater := 0
    result := [2]int{0, 0} // 存储最优边界

    for left < right {
        // 当前边界的蓄水高度
        height := min(s[left], s[right])

        // 计算当前区域的蓄水量
        water := height*(right-left-1) - sum(s[left+1:right])

        // 更新最大蓄水量和边界
        if water > maxWater {
            maxWater = water
            result = [2]int{left, right}
        }

        // 移动指针
        if s[left] < s[right] {
            left++
        } else {
            right--
        }
    }

    // 如果最大蓄水量为 0,则无法蓄水
    if maxWater == 0 {
        return "0"
    }

    // 输出结果
    return fmt.Sprintf("%d %d:%d", result[0], result[1], maxWater)
}

// 辅助函数:计算数组的和
func sum(arr []int) int {
    total := 0
    for _, num := range arr {
        total += num
    }
    return total
}

// 辅助函数:求最小值
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

// 主函数
func main() {
    var input []int
    var num int

    // 输入山脉高度数组
    for {
        _, err := fmt.Scan(&num)
        if err != nil {
            break
        }
        input = append(input, num)
    }

    // 调用函数并输出结果
    fmt.Println(maxWaterStorage(input))
}
def max_water_storage(s):
    n = len(s)
    if n < 3:  # 至少需要三个点才能形成蓄水区域
        return "0"

    left, right = 0, n - 1
    max_water = 0
    result = (0, 0)  # 存储最优边界

    while left < right:
        # 当前边界的蓄水高度
        height = min(s[left], s[right])

        # 计算当前区域的蓄水量
        water = height * (right - left - 1) - sum(s[left + 1:right])

        # 更新最大蓄水量和边界
        if water > max_water:
            max_water = water
            result = (left, right)

        # 移动指针
        if s[left] < s[right]:
            left += 1
        else:
            right -= 1

    # 如果最大蓄水量为 0,则无法蓄水
    if max_water == 0:
        return "0"

    # 输出结果
    return f"{result[0]} {result[1]}:{max_water}"


# 输入处理
if __name__ == "__main__":
    import sys
    input_data = list(map(int, sys.stdin.read().strip().split()))
    print(max_water_storage(input_data))

单调栈

#include <iostream>
#include <vector>
#include <stack>

using namespace std;

int main() {
    vector<int> height;
    int h;

    while (cin >> h) {
        height.push_back(h);
    }

    int n = height.size();
    stack<int> st;
    int x = -1, y = -1;
    int ans = 0, area = 0;

    for (int i = 0; i < n; i++) {
        int h = height[i];

        while (!st.empty() && h >= height[st.top()]) {
            int top = height[st.top()];
            st.pop();

            if (!st.empty()) {
                int d = min(height[st.top()], h) - top;
                int w = i - st.top() - 1;
                area += d * w;

                if (area > ans) {
                    ans = area;
                    x = st.top();
                    y = i;
                }

                if (area == ans && (y - x > i - st.top())) {
                    x = st.top();
                    y = i;
                }
            }
        }

        if (st.empty()) {
            area = 0;
        }
        st.push(i);
    }

    if (ans == 0) {
        cout << 0 << endl;
    } else {
        cout << x << " " << y << ":" << ans << endl;
    }

    return 0;
}