题解:学而思编程 懒惰的牛
【题目来源】
学而思编程:懒惰的牛
【题目描述】
这是一个炎热的夏日。懒洋洋的奶牛贝茜想将自己放置在田野中的某个位置,以便可以在短距离内尽可能多地吃到美味的草。
贝茜所在的田野中共有 \(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
【核心思想】
-
问题分析:给定 \(N\) 片草地,每片有位置 \(x_i\) 和草量 \(g_i\)。贝茜选择初始位置,可吃到距离不超过 \(K\) 的所有草地。求最大可吃草量。这是一个坐标离散化 + 前缀和 + 滑动窗口问题,关键在于将草地按位置排序后,用前缀和快速计算任意区间内的草量总和。
-
算法选择:
- 坐标映射:将草地位置映射到数组下标(\(x_i + 1\) 避免 \(0\)),草量存入对应位置
- 前缀和预处理:
s[i]表示位置 \(1\) 到 \(i\) 的累计草量 - 区间最值枚举:枚举每个位置作为中心,用前缀和 \(O(1)\) 计算覆盖区间 \([i-K, i+K]\) 的草量
-
关键步骤:
- 初始化:读取 \(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]
- 遍历 \(i\) 从 \(1\) 到
- 枚举中心位置:
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
-
时间/空间复杂度:
- 时间复杂度:\(O(N + V)\),\(V\) 为最大坐标值,坐标映射 \(O(N)\),前缀和 \(O(V)\),枚举 \(O(V)\)
- 空间复杂度:\(O(V)\),草量数组
a[1..len]和前缀和数组s[1..len]
-
前缀和区间查询的核心思想:
- 一维覆盖模型:贝茜的覆盖范围是中心点左右各 \(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
浙公网安备 33010602011771号