题解:AtCoder AT_awc0130_e Garden Maintenance

【题目来源】

AtCoder:E - Garden Maintenance

【题目描述】

Takahashi is a gardener in charge of caring for \(N\) flowers lined up in a row. The current height of the \(i\)-th flower is \(H_i\).

The client's request is: "For any contiguous \(K\) flowers, the difference between the maximum height and the minimum height among them should be at most \(D\)."

Takahashi can only cut the stems of the flowers (he cannot make them grow). Each flower can be cut to any non-negative integer height, but it cannot be made taller than its original height. That is, the final height \(H'_i\) of the \(i\)-th flower must be an integer satisfying \(0 \leq H'_i \leq H_i\).

Since Takahashi wants to keep the flowers as tall as possible, he wants to maximize the sum of the final heights of all flowers, \(H'_1 + H'_2 + \cdots + H'_N\).

Find the maximum possible sum of the final heights of the flowers when they are cut (or left as they are) to satisfy the conditions.

Formally, find the maximum value of \(\sum_{i=1}^{N} H'_i\) among all integer sequences \(H'_1, H'_2, \ldots, H'_N\) that satisfy the following conditions:

  • \(0 \leq H'_i \leq H_i\) for all \(i\) \((1 \leq i \leq N)\)
  • \(\max(H'_j, H'_{j+1}, \ldots, H'_{j+K-1}) - \min(H'_j, H'_{j+1}, \ldots, H'_{j+K-1}) \leq D\) for all \(j\) \((1 \leq j \leq N - K + 1)\)

高橋是一名园丁,负责照料一排 \(N\) 朵花。第 \(i\) 朵花的当前高度为 \(H_i\)

客户的要求是:"对于任意连续的 \(K\) 朵花,其中最大高度与最小高度之差不超过 \(D\)。"

高橋只能修剪花茎(他不能让花长高)。每朵花可以修剪到任意非负整数高度,但不能超过其原始高度。也就是说,第 \(i\) 朵花的最终高度 \(H'_i\) 必须是满足 \(0 \leq H'_i \leq H_i\) 的整数。

由于高橋希望花尽可能高,他想最大化所有花的最终高度之和 \(H'_1 + H'_2 + \cdots + H'_N\)

求将花修剪(或保持原样)以满足条件时,最终高度的最大可能总和。

形式化地,在满足以下条件的所有整数序列 \(H'_1, H'_2, \ldots, H'_N\) 中,求 \(\sum_{i=1}^{N} H'_i\) 的最大值:

  • 对所有 \(i\) \((1 \leq i \leq N)\)\(0 \leq H'_i \leq H_i\)
  • 对所有 \(j\) \((1 \leq j \leq N - K + 1)\)\(\max(H'_j, H'_{j+1}, \ldots, H'_{j+K-1}) - \min(H'_j, H'_{j+1}, \ldots, H'_{j+K-1}) \leq D\)

【输入】

\(N\) \(K\) \(D\)
\(H_1\) \(H_2\) \(\ldots\) \(H_N\)

  • The first line contains the number of flowers \(N\), the number of contiguous flowers \(K\), and the allowed height difference \(D\), separated by spaces.
  • The second line contains the current heights of the flowers \(H_1, H_2, \ldots, H_N\), separated by spaces.

【输出】

Print the maximum possible sum of the final heights of the flowers when they are cut to satisfy the conditions in a single line.

【输入样例】

5 3 2
3 1 4 1 5

【输出样例】

11

【核心思想】

  1. 问题分析:给定 \(N\) 朵花的初始高度 \(H_i\),只能修剪(降低高度)。要求任意连续 \(K\) 朵花的最大高度与最小高度之差 \(\leq D\)。目标是最大化最终高度之和。这是一个双向单调队列约束问题,关键在于每个位置的高度受左右两侧长度为 \(K\) 的滑动窗口共同约束,需从左右两个方向分别用单调队列传播约束。

  2. 算法选择

    • 单调队列(Monotonic Queue):维护滑动窗口内的最小值,\(O(1)\) 查询队首最小值
    • 双向扫描:先从左到右,利用左侧窗口约束当前高度;再从右到左,利用右侧窗口约束。两次扫描取交集得到最终高度
  3. 关键步骤

    • 读入数据\(N, K, D\),高度数组 \(a[1..N]\)
    • 从左到右扫描\(i\)\(1\)\(N\)):
      • 移除队首超出窗口范围(\(idx \leq i-K\))的元素
      • 若队列非空:a[i] = min(a[i], dq.front().v + D)(当前高度不能超过窗口最小值 \(+ D\)
      • 维护单调递增队列:从队尾移除 \(\geq a[i]\) 的元素,当前元素入队
    • 从右到左扫描\(i\)\(N\)\(1\)):
      • 移除队首超出窗口范围(\(idx \geq i+K\))的元素
      • 若队列非空:a[i] = min(a[i], dq.front().v + D)(对称约束)
      • 维护单调递增队列
    • 累加答案\(\sum_{i=1}^{N} a[i]\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N)\),每个元素最多入队出队各一次
    • 空间复杂度:\(O(N)\),高度数组和单调队列
  5. 双向单调队列约束的核心思想

    • 约束传播的对称性:位置 \(i\) 的高度受 \([i-K+1, i]\)\([i, i+K-1]\) 两个窗口约束。单向扫描只能处理一侧约束,双向扫描确保所有窗口约束都被满足
    • 单调队列维护窗口最小值:队列按高度递增、下标递增维护,队首始终是当前窗口的最小值。查询最小值 \(O(1)\),更新 \(O(1)\) 均摊
    • 高度上界收紧a[i] = min(a[i], min_window + D) 表示当前高度不能超过窗口内最低花的高度 \(+ D\),否则该窗口的极差会超过 \(D\)
    • 贪心最优性:每次将高度压到约束允许的最大值(取原始高度和约束上界的较小值),这是最大化总和的局部最优策略,由于约束的单调性,局部最优即全局最优
    • 适用于"滑动窗口极差约束下的序列调整"问题,核心在于双向传播约束和单调队列的 \(O(N)\) 维护

【算法标签】

单调队列

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long // 将int定义为long long,防止溢出
const int N = 200005; // 最大花朵数量
int n, k, d, ans; // n:花朵数量, k:连续窗口大小, d:允许的最大高度差, ans:最终答案
int a[N]; // a[i]:第i朵花的当前高度(后续会被修改为最终高度)

struct Node
{
    int v, idx; // v:高度值, idx:在原数组中的下标
};
deque<Node> dq; // 单调队列,用于维护滑动窗口内的最小值

signed main()
{
    cin >> n >> k >> d; // 读入花朵数量、窗口大小、最大允许高度差
    for (int i=1; i<=n; i++) // 读入每朵花的原始高度
        cin >> a[i];

    // 第一轮扫描:从左到右,利用左侧窗口约束当前花的高度
    for (int i=1; i<=n; i++)
    {
        // 移除队列中超出当前窗口范围(左侧k-1朵花之外)的元素
        while (!dq.empty() && dq.front().idx <= i-k)
            dq.pop_front();

        // 如果队列非空,当前花的高度不能超过窗口最小值+d
        // 即满足:当前高度 - 窗口最小高度 <= d
        if (!dq.empty())
        {
            a[i] = min(a[i], dq.front().v + d);
        }
        // 维护单调递增队列:移除队尾所有大于等于当前高度的元素
        while (!dq.empty() && dq.back().v >= a[i])
            dq.pop_back();
        // 当前元素入队
        dq.push_back({a[i], i});
    }

    dq.clear(); // 清空队列,准备第二轮扫描

    // 第二轮扫描:从右到左,利用右侧窗口约束当前花的高度
    for (int i=n; i>=1; i--)
    {
        // 移除队列中超出当前窗口范围(右侧k-1朵花之外)的元素
        while (!dq.empty() && dq.front().idx >= i+k)
            dq.pop_front();

        // 如果队列非空,当前花的高度不能超过窗口最小值+d
        // 即满足:窗口最小高度 - 当前高度 <= d(等价于当前高度 >= 窗口最小高度 - d)
        // 从右往左看,就是当前高度 <= 右侧窗口最小值 + d
        if (!dq.empty())
            a[i] = min(a[i], dq.front().v + d);
        // 维护单调递增队列
        while (!dq.empty() && dq.back().v >=a[i])
            dq.pop_back();
        // 当前元素入队
        dq.push_back({a[i], i});
    }

    // 累加所有花的最终高度,得到最大可能总和
    for (auto x : a)
        ans += x;
    cout << ans << endl; // 输出答案
    return 0;
}

【运行结果】

5 3 2
3 1 4 1 5
posted @ 2026-08-10 12:24  团爸讲算法  阅读(8)  评论(0)    收藏  举报