#P3393. 第2题-跳水评分
-
1000ms
Tried: 44
Accepted: 17
Difficulty: 7
所属公司 :
京东
时间 :2025年8月16日
-
算法标签>滑动窗口
第2题-跳水评分
解题思路
-
核心转化:固定窗口大小为 m,最终成绩为 (sum - min - max) / (m - 2)。因为分母固定,最大化平均值等价于最大化分子
sum - min - max。 -
滑动窗口:
-
维护窗口和
wsum(进入加、移出减)。 -
维护窗口最小值与最大值:用两个单调队列
- 递增队列维护最小值(队头最小)
- 递减队列维护最大值(队头最大)
-
每次窗口形成(i >= m-1)时,取
mn = a[inc.front()]mx = a[dec.front()]- 当前得分和
val = wsum - mn - mx
-
记录最大
val的窗口起点;若相等取起点更小者。
-
-
正确性:分母固定,最大化分子即可;单调队列在均摊 O(1) 时间维护窗口极值;并且当所有元素相等时,
min==max也没问题,等价于删去两个相同值。
复杂度分析
- 时间复杂度:O(n),每个元素进出各单调队列至多一次。
- 空间复杂度:O(m)(队列存索引),数组 O(n)。
Python 代码
import sys
from collections import deque
def main():
data = sys.stdin.read().strip().split()
n, m = map(int, data[:2])
a = list(map(int, data[2:]))
inc = deque() # 维护最小值(递增)
dec = deque() # 维护最大值(递减)
wsum = 0
best_val = None
best_l = 0
for i, x in enumerate(a):
wsum += x
# 维护递增队列(最小值)
while inc and a[inc[-1]] >= x:
inc.pop()
inc.append(i)
# 维护递减队列(最大值)
while dec and a[dec[-1]] <= x:
dec.pop()
dec.append(i)
# 窗口左端
if i >= m:
# 移出 i-m 位置
wsum -= a[i - m]
# 弹出过期索引
while inc and inc[0] <= i - m:
inc.popleft()
while dec and dec[0] <= i - m:
dec.popleft()
if i >= m - 1:
mn = a[inc[0]]
mx = a[dec[0]]
val = wsum - mn - mx
l = i - m + 1
if best_val is None or val > best_val or (val == best_val and l < best_l):
best_val = val
best_l = l
print(best_l + 1)
if __name__ == "__main__":
main()
Java 代码
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
long[] a = new long[n];
st = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) a[i] = Long.parseLong(st.nextToken());
Deque<Integer> inc = new ArrayDeque<>(); // 递增,取最小
Deque<Integer> dec = new ArrayDeque<>(); // 递减,取最大
long wsum = 0;
long bestVal = Long.MIN_VALUE;
int bestL = 0;
boolean inited = false;
for (int i = 0; i < n; i++) {
long x = a[i];
wsum += x;
while (!inc.isEmpty() && a[inc.peekLast()] >= x) inc.pollLast();
inc.addLast(i);
while (!dec.isEmpty() && a[dec.peekLast()] <= x) dec.pollLast();
dec.addLast(i);
if (i >= m) {
wsum -= a[i - m];
}
while (!inc.isEmpty() && inc.peekFirst() <= i - m) inc.pollFirst();
while (!dec.isEmpty() && dec.peekFirst() <= i - m) dec.pollFirst();
if (i >= m - 1) {
long mn = a[inc.peekFirst()];
long mx = a[dec.peekFirst()];
long val = wsum - mn - mx;
int l = i - m + 1;
if (!inited || val > bestVal || (val == bestVal && l < bestL)) {
bestVal = val;
bestL = l;
inited = true;
}
}
}
System.out.println(bestL + 1);
}
}
C++ 代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
if (!(cin >> n >> m)) return 0;
vector<long long> a(n);
for (int i = 0; i < n; ++i) cin >> a[i];
deque<int> inc, dec; // inc维护最小,dec维护最大
long long wsum = 0;
long long bestVal = LLONG_MIN;
int bestL = 0;
bool inited = false;
for (int i = 0; i < n; ++i) {
long long x = a[i];
wsum += x;
while (!inc.empty() && a[inc.back()] >= x) inc.pop_back();
inc.push_back(i);
while (!dec.empty() && a[dec.back()] <= x) dec.pop_back();
dec.push_back(i);
if (i >= m) {
wsum -= a[i - m];
}
while (!inc.empty() && inc.front() <= i - m) inc.pop_front();
while (!dec.empty() && dec.front() <= i - m) dec.pop_front();
if (i >= m - 1) {
long long mn = a[inc.front()];
long long mx = a[dec.front()];
long long val = wsum - mn - mx;
int l = i - m + 1;
if (!inited || val > bestVal || (val == bestVal && l < bestL)) {
bestVal = val;
bestL = l;
inited = true;
}
}
}
cout << bestL + 1 << "\n";
return 0;
}
- 输出即为题目所需答案。
题目内容
在一场跳水比赛中,共有 n 位裁判依次为选手打分(打分均为非负整数)。根据比赛规则,需要从所有裁判的打分中,选取连续的 m 个打分来计算选手的最终成绩。具体计算方式为:从这 m 个打分里去掉 1 个最高分和 1 个最低分,最后取剩余分数的平均值作为该选手的最终成绩。
现在需要找到所有可能的连续 m 个打分区间中,最终成绩最高的那个区间,并输出该区间的起始裁判编号(从 1 开始计数)。如果有多个区间的最终成绩相同,输出起始编号最小的那个。
输入描述
第一行包含两个正整数 n 和 m(3≤m≤n≤105) 分别表示裁判总数和选取的连续打分个数。
第二行包含 n 个非负整数 a1,a2,...,an(0≤ai≤109),表示每位裁判的打分。
输出描述
输出一个正整数,表示最终成绩最高的区间的起始裁判编号。
样例1
输入
6 4
4 2 8 5 9 3
输出
2
样例2
输入
5 3
4 3 4 4 4
输出
1