班次连窗日均
拼多多 9月13号 笔试真题 第二题
拼多多真题目录点击查看: 拼多多笔试真题题库目录|笔试题库 + 算法考点详解
题目内容
推理集群连续跑了 m m m 个班次,第 p p p 个班次的净值记为 v p v_p vp(可以为负,表示当班调度损耗)。要在班次序列上截取一段连续窗口 q , s q,s q,s,窗口长度必须至少为 w w w,否则调度系统不认这段窗口。
窗口的班均净值定义为
⌊ v q + v q + 1 + ⋯ + v s s − q + 1 ⌋ \left\lfloor \frac{v_q + v_{q+1} + \dots + v_s}{s-q+1} \right\rfloor ⌊s−q+1vq+vq+1+⋯+vs⌋
其中 ⌊ ⋅ ⌋ \lfloor \cdot \rfloor ⌊⋅⌋ 表示不超过该实数的最大整数,例如 ⌊ − 11 / 2 ⌋ = ⌊ − 5.5 ⌋ = − 6 \lfloor -11/2 \rfloor = \lfloor -5.5 \rfloor = -6 ⌊−11/2⌋=⌊−5.5⌋=−6。
请在所有长度合法的窗口里,把班均净值做到最大,并输出这个最大值。保证 w ≤ m w \le m w≤m,因此至少存在一个合法窗口。
输入描述
首行给出整数 g g g( 1 ≤ g ≤ 10 1\le g\le 10 1≤g≤10),表示随后有多少组数据。
每一组的格式如下:
该组开头给出整数 m m m( 1 ≤ m ≤ 10 5 1\le m\le 10^5 1≤m≤105),即班次个数。
下一行给出整数 w w w( 1 ≤ w ≤ m 1\le w\le m 1≤w≤m),即窗口最短长度。
第三行 m m m 个整数 v 1 , v 2 , ... , v m v_1,v_2,\dots,v_m v1,v2,...,vm( ∣ v p ∣ ≤ 10 9 |v_p|\le 10^9 ∣vp∣≤109),用逗号分隔,依次为各班次净值。
输出描述
输出一行,包含 g g g 个整数,相邻两项以空格分隔,依次为每一组的最大班均净值。
样例1
输入
3
6
2
4,-1,8,-6,3,5
3
2
-2,-9,-4
4
4
10,-1,-1,10
输出
4 -5 4
说明
第一组最短长度为 2 2 2,取最后两个班次 { 3 , 5 } \{3,5\} {3,5},和为 8 8 8,班均为 ⌊ 8 / 2 ⌋ = 4 \lfloor 8/2 \rfloor = 4 ⌊8/2⌋=4。更长的窗口都到不了 4 4 4。
第二组全是亏损。取整段 { − 2 , − 9 , − 4 } \{-2,-9,-4\} {−2,−9,−4},和为 − 15 -15 −15,班均为 ⌊ − 15 / 3 ⌋ = − 5 \lfloor -15/3 \rfloor = -5 ⌊−15/3⌋=−5。只取 { − 2 , − 9 } \{-2,-9\} {−2,−9} 得到 ⌊ − 11 / 2 ⌋ = − 6 \lfloor -11/2 \rfloor = -6 ⌊−11/2⌋=−6,更差。
第三组最短长度等于班次数,只能取整段,和为 18 18 18,班均为 ⌊ 18 / 4 ⌋ = 4 \lfloor 18/4 \rfloor = 4 ⌊18/4⌋=4。
题解
思路
解题算法:二分 + 前缀和
- 二分最大班均净值
mid,使用前缀和判断是否存在长度至少为w的子数组,使平均值 ≥ mid。- 存在:
left = mid + 1 - 不存在:
right = mid - 1
- 存在:
- 判断是否存在长度至少为
w的子数组,使平均值 ≥ mid,可以将每个数变为ai = vi - x,问题变为是否存在一个长度至少为w的连续子数组,满足元素和>= 0?使用前缀和判断是否存在prefix[j] - prefix[i] >=0,其中j - i >= w - 算法总体时间复杂度为
O(mlog10^9)
C++
cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// 判断是否存在长度至少为 w 的窗口,使平均值 >= x
bool check(const vector<ll>& v, int w, ll x) {
int m = v.size();
vector<ll> prefix(m + 1, 0);
for (int i = 1; i <= m; i++) {
prefix[i] = prefix[i - 1] + v[i - 1] - x;
}
ll minPrefix = 0;
for (int i = w; i <= m; i++) {
// j <= i - w
minPrefix = min(minPrefix, prefix[i - w]);
// prefix[i] - prefix[j] >= 0
if (prefix[i] >= minPrefix) {
return true;
}
}
return false;
}
ll solve(int m, int w, vector<ll>& v) {
ll left = -1000000000;
ll right = 1000000000;
while (left <= right) {
ll mid = left + (right - left) / 2;
if (check(v, w, mid)) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int g;
cin >> g;
vector<ll> ans;
while (g--) {
int m, w;
cin >> m;
cin >> w;
vector<ll> v(m);
for (int i = 0; i < m; i++) {
char comma;
cin >> v[i];
if (i + 1 < m) {
cin >> comma;
}
}
ans.push_back(solve(m, w, v));
}
for (int i = 0; i < ans.size(); i++) {
if (i > 0) {
cout << " ";
}
cout << ans[i];
}
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
// 判断是否存在长度至少为 w 的窗口,使平均值 >= x
static boolean check(long[] v, int w, long x) {
int m = v.length;
long[] prefix = new long[m + 1];
for (int i = 1; i <= m; i++) {
prefix[i] = prefix[i - 1] + v[i - 1] - x;
}
long minPrefix = 0;
for (int i = w; i <= m; i++) {
// j <= i - w
minPrefix = Math.min(minPrefix, prefix[i - w]);
// prefix[i] - prefix[j] >= 0
if (prefix[i] >= minPrefix) {
return true;
}
}
return false;
}
static long solve(int m, int w, long[] v) {
long left = -1000000000L;
long right = 1000000000L;
while (left <= right) {
long mid = left + (right - left) / 2;
if (check(v, w, mid)) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int g = sc.nextInt();
long[] ans = new long[g];
for (int t = 0; t < g; t++) {
int m = sc.nextInt();
int w = sc.nextInt();
String[] parts = sc.next().split(",");
long[] v = new long[m];
for (int i = 0; i < m; i++) {
v[i] = Long.parseLong(parts[i]);
}
ans[t] = solve(m, w, v);
}
for (int i = 0; i < g; i++) {
if (i > 0) {
System.out.print(" ");
}
System.out.print(ans[i]);
}
}
}
python
python
import sys
# 判断是否存在长度至少为 w 的窗口,使平均值 >= x
def check(v, w, x):
m = len(v)
prefix = [0] * (m + 1)
for i in range(1, m + 1):
prefix[i] = prefix[i - 1] + v[i - 1] - x
min_prefix = 0
for i in range(w, m + 1):
# j <= i - w
min_prefix = min(min_prefix, prefix[i - w])
# prefix[i] - prefix[j] >= 0
if prefix[i] >= min_prefix:
return True
return False
def solve(m, w, v):
left = -1000000000
right = 1000000000
while left <= right:
mid = left + (right - left) // 2
if check(v, w, mid):
left = mid + 1
else:
right = mid - 1
return right
def main():
input = sys.stdin.readline
g = int(input())
ans = []
for _ in range(g):
m = int(input())
w = int(input())
v = list(map(int, input().strip().split(",")))
ans.append(solve(m, w, v))
print(*ans)
if __name__ == "__main__":
main()
javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const lines = [];
rl.on('line', line => {
lines.push(line.trim());
});
rl.on('close', () => {
let index = 0;
// 判断是否存在长度至少为 w 的窗口,使平均值 >= x
function check(v, w, x) {
const m = v.length;
const prefix = new Array(m + 1).fill(0);
for (let i = 1; i <= m; i++) {
prefix[i] = prefix[i - 1] + v[i - 1] - x;
}
let minPrefix = 0;
for (let i = w; i <= m; i++) {
// j <= i - w
minPrefix = Math.min(minPrefix, prefix[i - w]);
// prefix[i] - prefix[j] >= 0
if (prefix[i] >= minPrefix) {
return true;
}
}
return false;
}
function solve(m, w, v) {
let left = -1000000000;
let right = 1000000000;
while (left <= right) {
const mid = Math.floor(left + (right - left) / 2);
if (check(v, w, mid)) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
const g = parseInt(lines[index++]);
const ans = [];
for (let t = 0; t < g; t++) {
const m = parseInt(lines[index++]);
const w = parseInt(lines[index++]);
const v = lines[index++].split(',').map(Number);
ans.push(solve(m, w, v));
}
console.log(ans.join(' '));
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// 判断是否存在长度至少为 w 的窗口,使平均值 >= x
func check(v []int64, w int, x int64) bool {
m := len(v)
prefix := make([]int64, m+1)
for i := 1; i <= m; i++ {
prefix[i] = prefix[i-1] + v[i-1] - x
}
var minPrefix int64 = 0
for i := w; i <= m; i++ {
// j <= i - w
if prefix[i-w] < minPrefix {
minPrefix = prefix[i-w]
}
// prefix[i] - prefix[j] >= 0
if prefix[i] >= minPrefix {
return true
}
}
return false
}
func solve(m int, w int, v []int64) int64 {
var left int64 = -1000000000
var right int64 = 1000000000
for left <= right {
mid := left + (right-left)/2
if check(v, w, mid) {
left = mid + 1
} else {
right = mid - 1
}
}
return right
}
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
// 读取所有非空行
lines := make([]string, 0)
for {
line, err := in.ReadString('\n')
line = strings.TrimSpace(line)
if line != "" {
lines = append(lines, line)
}
if err != nil {
break
}
}
index := 0
// 第一行是测试组数
g, _ := strconv.Atoi(lines[index])
index++
ans := make([]int64, g)
for t := 0; t < g; t++ {
// 第一行:m
m, _ := strconv.Atoi(lines[index])
index++
// 第二行:w
w, _ := strconv.Atoi(lines[index])
index++
// 第三行:数组
parts := strings.Split(lines[index], ",")
index++
v := make([]int64, m)
for i := 0; i < m; i++ {
v[i], _ = strconv.ParseInt(strings.TrimSpace(parts[i]), 10, 64)
}
ans[t] = solve(m, w, v)
}
for i := 0; i < g; i++ {
if i > 0 {
fmt.Fprint(out, " ")
}
fmt.Fprint(out, ans[i])
}
}