弹性分桶(Py/Java/C++/Js/Go)题解
美团研发岗 0509笔试 第三题
题目内容
给定nnn个非负整数a1,a2,...,ana_1,a_2,\dots,a_na1,a2,...,an,你可以在分组前对这些数进行任意重排。
你需要把所有数划分到若干个桶,每个桶内包含若干个数,设某个桶内共有ttt个数,桶内最大值为MMM,最小值为mmm,则该桶必须满足:
1 ≤ t ≤ L1 \leq t \leq L1 ≤ t ≤ L
M − m ≤ D + E × (t − 1)M - m \leq D + E \times (t - 1)M − m ≤ D + E × (t − 1)
每个数必须且只能属于一个桶,桶与桶之间互不相交,且并集为全部nnn个数,你的目标是使桶的数量尽可能少,请输出最少桶数。
输入描述
每个测试文件包含多组测试数据,第一行输入一个整数 T(1 ≤ T ≤ 105)T(1 \leq T \leq 10^5)T(1 ≤ T ≤ 105) 表示数据组数,每组测试数据描述如下:
第一行输入四个整数 n, D, E, L(1 ≤ n ≤ 2 × 105, 0 ≤ D, E ≤ 109, 1 ≤ L ≤ n)n, D, E, L(1 \leq n \leq 2 \times 10^5, 0 \leq D, E \leq 10^9, 1 \leq L \leq n)n, D, E, L(1 ≤ n ≤ 2 × 105, 0 ≤ D, E ≤ 109, 1 ≤ L ≤ n)。
第二行输入 nnn 个整数 a1, a2, ..., an(0 ≤ ai ≤ 109)a_1, a_2, \dots, a_n(0 \leq a_i \leq 10^9)a1, a2, ..., an(0 ≤ ai ≤ 109)。
保证所有测试数据的 nnn 之和不超过 2 × 1052 \times 10^52 × 105。
输出描述
对于每组测试数据,输出一行一个整数,表示最少需要多少个桶。
样例1
输入
2
7 3 1 3
1 2 3 7 8 10 10
7 0 0 5
5 5 5 6 6 6 6
输出
3
2
说明
第一组数据的一个最优划分为1,2,3,7,8,10,10\left1,2,3\\right, \left7,8,10\\right, \left10\\right1,2,3,7,8,10,10,因此答案为333。 第二组数据中由于D=0,E=0D = 0, E = 0D=0,E=0,同一桶内必须全部相等,最少需要222个桶。
题解
思路
贪心 + 双指针
- 桶约束只和
最大值-最小值和元素数量相关 - 将桶中元素升序排序,其中
a[l]为最小值,a[r]为最大值,其中t = r - l + 1, 上述约束变为t <= L,a[r] - a[l] <= D + E * (t - 1)=>a[r] <= a[l] + D + E * (t - 1).可以观察l固定不变情况下,r越大,允许的ar的值也越大。 - 想要桶尽可能少=》每个桶装的数量尽可能多,所以可以使用
贪心思维尽可能让每个桶装尽可能多元素,不能装时再加入新的桶。具体逻辑为:- 初始对所有元素进行排序
- 每一轮尝试新桶时,固定左边解为
l = i, 然后根据2不断尝试扩展右边界r当下面某一个条件不满足,加入新的桶重复执行r - l + 1 > La[r] > a[l] + D + E * (r - i + 1)
- 根据3处理之后,可以得到最终桶的数量
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
long long D, E;
int L;
cin >> n >> D >> E >> L;
vector<long long> a(n);
for (auto &x : a) cin >> x;
// 升序排序
sort(a.begin(), a.end());
int ans = 0;
int i = 0;
while (i < n) {
int j = i;
while (j + 1 < n && (j - i + 1 < L)) {
long long limit = a[i] + D + E * (j + 1 - i);
if (a[j + 1] <= limit) j++;
else break;
}
ans++;
i = j + 1;
}
cout << ans << "\n";
}
return 0;
}
java
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));
StringBuilder sb = new StringBuilder();
int T = Integer.parseInt(br.readLine().trim());
while (T-- > 0) {
String[] parts = br.readLine().trim().split(" ");
int n = Integer.parseInt(parts[0]);
long D = Long.parseLong(parts[1]);
long E = Long.parseLong(parts[2]);
int L = Integer.parseInt(parts[3]);
long[] a = new long[n];
String[] arr = br.readLine().trim().split(" ");
for (int i = 0; i < n; i++) {
a[i] = Long.parseLong(arr[i]);
}
// 升序排序
Arrays.sort(a);
int ans = 0;
int i = 0;
while (i < n) {
int j = i;
while (j + 1 < n && (j - i + 1 < L)) {
long limit = a[i] + D + E * (j + 1 - i);
if (a[j + 1] <= limit) j++;
else break;
}
ans++;
i = j + 1;
}
sb.append(ans).append("\n");
}
System.out.print(sb.toString());
}
}
python
python
import sys
input = sys.stdin.readline
T = int(input())
for _ in range(T):
n, D, E, L = map(int, input().split())
a = list(map(int, input().split()))
# 升序排序
a.sort()
ans = 0
i = 0
while i < n:
j = i
while j + 1 < n and (j - i + 1 < L):
limit = a[i] + D + E * (j + 1 - i)
if a[j + 1] <= limit:
j += 1
else:
break
ans += 1
i = j + 1
print(ans)
javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
rl.on("line", (line) => {
input.push(line.trim());
});
rl.on("close", () => {
let idx = 0;
let T = Number(input[idx++]);
while (T--) {
let [n, D, E, L] = input[idx++].split(" ").map(Number);
let a = input[idx++].split(" ").map(Number);
// 升序排序
a.sort((x, y) => x - y);
let ans = 0;
let i = 0;
while (i < n) {
let j = i;
while (j + 1 < n && (j - i + 1 < L)) {
let limit = a[i] + D + E * (j + 1 - i);
if (a[j + 1] <= limit) j++;
else break;
}
ans++;
i = j + 1;
}
console.log(ans);
}
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
"sort"
)
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var T int
fmt.Fscan(in, &T)
for T > 0 {
T--
var n int
var D, E int64
var L int
fmt.Fscan(in, &n, &D, &E, &L)
a := make([]int64, n)
for i := 0; i < n; i++ {
fmt.Fscan(in, &a[i])
}
// 升序排序
sort.Slice(a, func(i, j int) bool {
return a[i] < a[j]
})
ans := 0
i := 0
for i < n {
j := i
for j+1 < n && (j-i+1 < L) {
limit := a[i] + D + E*int64(j+1-i)
if a[j+1] <= limit {
j++
} else {
break
}
}
ans++
i = j + 1
}
fmt.Fprintln(out, ans)
}
}