题解:学而思编程 懒惰的牛

【题目来源】

学而思编程:懒惰的牛

【题目描述】

这是一个炎热的夏日。懒洋洋的奶牛贝茜想将自己放置在田野中的某个位置,以便可以在短距离内尽可能多地吃到美味的草。

贝茜所在的田野中共有 \(N\) 片草地,我们可以将田野视作一个一维数轴。第 \(i\) 片草地中包含 \(g_i\) 单位的青草,位置坐标为 \(x_i\)。不同草地的位置不同。

贝茜想选取田野中的某个点作为她的初始位置(可能是某片草地所在的点)。只有一片草地与她的初始位置的距离不超过 \(K\) 时,贝茜才能吃到那片草地上的草。

如果贝茜选择最佳初始位置,请确定她可以吃到的青草最大数量。

【输入】

第一行包含两个整数 \(N\)\(K\)

接下来 \(N\) 行,每行描述一片草地,包含两个整数 \(g_i\)\(x_i\)

【输出】

输出如果贝茜选择最佳初始位置,则她可以吃到的青草最大数量。

【输入样例】

4 3
4 7
10 15
2 2
5 1

【输出样例】

11

【核心思想】

  1. 问题分析:给定 \(N\) 片草地,每片有位置 \(x_i\) 和草量 \(g_i\)。贝茜选择初始位置,可吃到距离不超过 \(K\) 的所有草地。求最大可吃草量。这是一个坐标离散化 + 前缀和 + 滑动窗口问题,关键在于将草地按位置排序后,用前缀和快速计算任意区间内的草量总和。

  2. 算法选择

    • 坐标映射:将草地位置映射到数组下标(\(x_i + 1\) 避免 \(0\)),草量存入对应位置
    • 前缀和预处理s[i] 表示位置 \(1\)\(i\) 的累计草量
    • 区间最值枚举:枚举每个位置作为中心,用前缀和 \(O(1)\) 计算覆盖区间 \([i-K, i+K]\) 的草量
  3. 关键步骤

    • 初始化:读取 \(N\)(草地数)和 \(K\)(最大距离)
    • 坐标映射与草量存储
      • len = 0
      • 遍历 \(i\)\(1\)\(N\)
        • 读取 \(g_i\)\(x_i\)
        • a[x_i + 1] = g_i:位置偏移 \(+1\) 使下标从 \(1\) 开始
        • len = max(len, x_i + 1):记录最大坐标
    • 计算前缀和
      • 遍历 \(i\)\(1\)len
        • s[i] = s[i-1] + a[i]
    • 枚举中心位置
      • ans = 0
      • 遍历 \(i\)\(1\)len
        • l = max(1, i - K):覆盖区间左端点
        • r = min(i + K, len):覆盖区间右端点
        • sum = s[r] - s[l-1]:区间 \([l, r]\) 内的草量
        • ans = max(ans, sum):更新最大值
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N + V)\)\(V\) 为最大坐标值,坐标映射 \(O(N)\),前缀和 \(O(V)\),枚举 \(O(V)\)
    • 空间复杂度:\(O(V)\),草量数组 a[1..len] 和前缀和数组 s[1..len]
  5. 前缀和区间查询的核心思想

    • 一维覆盖模型:贝茜的覆盖范围是中心点左右各 \(K\) 的区间,问题转化为求所有长度为 \(2K+1\) 的滑动窗口中的最大元素和
    • 坐标离散化:将稀疏的草地位置映射到连续数组下标,前缀和可在离散化后的坐标上直接计算
    • 边界裁剪max(1, i-K)min(i+K, len) 处理区间越界,保证查询合法
    • 枚举最优性:覆盖区间的最优中心必然在某片草地位置或附近,枚举所有位置不会遗漏最优解
    • 适用于"一维区间覆盖 + 权重求和最大化"类问题,前缀和将区间查询优化到 \(O(1)\)

【算法标签】

前缀和

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;

int a[N], s[N];  // a数组记录每个位置的冰淇淋数量,s数组是前缀和

int main()
{
    int n, k;
    cin >> n >> k;
    int len = 0;  // 记录最大的位置坐标
    for (int i = 1; i <= n; i++)
    {
        int g, x;
        cin >> g >> x;
        a[x + 1] = g;  // 位置从1开始,所以x+1
        len = max(len, x + 1);  // 更新最大位置
    }
    // 计算前缀和
    for (int i = 1; i <= len; i++)
    {
        s[i] = s[i - 1] + a[i];
    }
    int ans = 0;
    // 遍历每个位置作为中心
    for (int i = 1; i <= len; i++)
    {
        // 计算覆盖范围
        int l = max(1, i - k), r = min(i + k, len);
        // 使用前缀和快速计算区间和
        int sum = s[r] - s[l - 1];
        ans = max(ans, sum);
    }
    cout << ans << endl;
    return 0;
}

【运行结果】

4 3
4 7
10 15
2 2
5 1
11
posted @ 2026-07-05 14:20  团爸讲算法  阅读(6)  评论(0)    收藏  举报