先把同一坐标上的星星亮度合并,再把题目转成一维数组上固定长度窗口的最大区间和,用前缀和线性扫描即可。
OJ: luogu
题目 ID: P3353
难度:普及-
标签:前缀和滑动窗口数组模拟
日期: 2026-06-21 01:50
题意
数轴上有
窗户一次能看到一个宽度为
题目要求我们移动窗户位置,使窗口内所有星星的亮度和最大,并输出这个最大值。
要注意两件事:
- 同一个位置上可能有多颗星星
- 当
时,实际上只能看到某一个精确位置上的星星
思路
先看一个最直接、最容易理解的暴力写法:
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100000 + 5;
int n, w;
int x[MAXN];
int b[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> w;
int min_pos = 1000000000;
int max_pos = 0;
for (int i = 1; i <= n; i++) {
cin >> x[i] >> b[i];
if (x[i] < min_pos) {
min_pos = x[i];
}
if (x[i] > max_pos) {
max_pos = x[i];
}
}
int answer = 0;
if (w == 0) {
// 没有边缘时,只能选中一个精确位置。
for (int pos = min_pos; pos <= max_pos; pos++) {
int current = 0;
for (int i = 1; i <= n; i++) {
if (x[i] == pos) {
current += b[i];
}
}
if (current > answer) {
answer = current;
}
}
} else {
int half = w / 2;
// 枚举窗户中心位置,直接判断每颗星星是否落在窗口内。
for (int center = min_pos - half; center <= max_pos + half; center++) {
int left = center - half;
int right = center + half;
int current = 0;
for (int i = 1; i <= n; i++) {
if (left <= x[i] && x[i] <= right) {
current += b[i];
}
}
if (current > answer) {
answer = current;
}
}
}
cout << answer << '\n';
return 0;
}brute.cpp 的思路很直白:
- 枚举窗户位置
- 对每个位置重新扫描所有星星
- 判断这颗星星是否落在窗口内
这个做法是对的,但会重复扫描很多次,复杂度太高,只适合做理解题意和小数据对拍。
真正的关键是先做一次坐标聚合。
如果同一个位置上有多颗星星,那么无论窗户怎么放,只要覆盖到这个位置,它们的贡献都会一起加进去。
所以可以先定义:
这样题目就从“很多星星”变成了“一个一维数组”:
- 若
,答案就是 的最大值 - 若
,答案就是连续 个整数位置上的最大区间和
接下来做前缀和:
于是任意区间
最后枚举每个窗口的右端点
- 左端点
- 当前窗口和就是
一路取最大值即可。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXX = 100000 + 5;
int n, w;
int sum_brightness[MAXX];
int prefix_sum[MAXX];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> w;
int max_pos = 0;
for (int i = 1; i <= n; i++) {
int x, b;
cin >> x >> b;
sum_brightness[x] += b;
if (x > max_pos) {
max_pos = x;
}
}
for (int i = 1; i <= max_pos; i++) {
prefix_sum[i] = prefix_sum[i - 1] + sum_brightness[i];
}
int answer = 0;
if (w == 0) {
// W=0 时,窗户只能看到某一个精确位置上的星星。
for (int i = 1; i <= max_pos; i++) {
if (sum_brightness[i] > answer) {
answer = sum_brightness[i];
}
}
} else {
// 题目保证 W 为奇数,此时长度为 W 的闭区间恰好覆盖连续 W 个整数位置。
for (int right = 1; right <= max_pos; right++) {
int left = right - w + 1;
if (left < 1) {
left = 1;
}
int current = prefix_sum[right] - prefix_sum[left - 1];
if (current > answer) {
answer = current;
}
}
}
cout << answer << '\n';
return 0;
}复杂度
设最大坐标为
- 读入并聚合:
- 求前缀和:
- 枚举所有窗口:
总时间复杂度:
空间复杂度:
总结
这题的难点不在算法本身,而在建模转换:
- 原题说的是“星星散落在数轴上”
- 真正做时要先把它变成“每个位置有多少总亮度”
一旦完成这一步,题目就退化成了非常标准的定长最大区间和问题。
