最小化峰值干扰
小红书 9月10号 笔试真题 第一题
题目描述
要将 mmm 项探测任务按给定顺序安排到连续 nnn 个时隙。第 jjj 个时隙的干扰强度为 aja_jaj,第 iii 项任务占用连续 bib_ibi 个时隙。记 lil_ili 为第 iii 项任务的起始时隙,则其占用区间为 li,li+bi−1l_i,l_i+b_i-1li,li+bi−1。
安排须满足:
- 任务占用互不重叠:对任意 i<ji<ji<j,有 li+bi−1<ljl_i+b_i-1<l_jli+bi−1<lj;任务之间可空出任意个时隙。
- 任务保持给定顺序:l1<l2<⋯<lml_1<l_2<\dots<l_ml1<l2<⋯<lm。
- 均落在时隙范围内:1≤li1\le l_i1≤li 且 li+bi−1≤nl_i+b_i-1\le nli+bi−1≤n。
峰值干扰为
maxi=1mmaxj=lili+bi−1aj\max_{i=1}^{m}\max_{j=l_i}^{l_i+b_i-1} a_jmaxi=1mmaxj=lili+bi−1aj
求所有合法安排下峰值干扰的最小值。
输入描述
第一行一个正整数 TTT,表示测试数据组数。
对于每组测试数据:
第一行两个正整数 n,mn,mn,m,表示时隙数和任务数。
第二行 nnn 个正整数 a1,a2,...,ana_1,a_2,\dots,a_na1,a2,...,an,表示每个时隙的干扰强度。
第三行 mmm 个正整数 b1,b2,...,bmb_1,b_2,\dots,b_mb1,b2,...,bm,表示每项任务占用的时隙数。
输出描述
对于每组测试数据,输出一行一个整数,表示峰值干扰的最小值。
样例1
输入
2
7 2
8 1 3 5 2 1 4
2 3
6 3
2 8 1 1 3 1
1 1 2
输出
4
3
说明
第一组:第一项任务放在 2,32,32,3(覆盖 1,31,31,3,最大值 333),第二项放在 5,75,75,7(覆盖 2,1,42,1,42,1,4,最大值 444)。峰值干扰为 max(3,4)=4\max(3,4)=4max(3,4)=4。若要求峰值不超过 333,则时隙 111、444、777 不可用,剩余连续段长度不足以放下长度为 333 的第二项任务。
第二组:三项任务分别放在时隙 111、333 与 4,54,54,5(覆盖 222;111;1,31,31,3),峰值干扰为 max(2,1,3)=3\max(2,1,3)=3max(2,1,3)=3。若要求峰值不超过 222,则时隙 222、555 不可用,无法为第三项任务找到长度为 222 的连续段。
数据范围
1≤T≤2000001\le T\le 2000001≤T≤200000,所有测试数据的 nnn 之和 ≤200000\le 200000≤200000,1≤n1\le n1≤n,1≤m1\le m1≤m,1≤ai≤1091\le a_i\le 10^91≤ai≤109,1≤bi≤n1\le b_i\le n1≤bi≤n,∑bi≤n\sum b_i\le n∑bi≤n。
题解和思路
思路
实现思路:二分
- 峰值x的大小具备单调性,所以可以使用二分,规律为
- 如果
x可以满足分配,那么更大的x一定可以满足分配 - 如果
x不可以满足分配,那么更小的x一定不可以满足分配。
- 如果
- 判断
x枚举的是否可行,直接进行贪心线性分配检验就行,从左至右扫描连续a[i] <= x的长度,当长度满足当前待分配任务时,就进行分配。最终检验是否所有任务都可以被分配就行。 - 时间复杂度为
O(nlog)
C++
cpp
#include<bits/stdc++.h>
using namespace std;
// 贪心线性检验不超过x情况能否全部分配
bool check(int x, vector<int>& a, vector<int>& b) {
int n = a.size();
int m = b.size();
int id = 0;
int len = 0;
for (int i = 0; i < n; i++) {
if (a[i] <= x) {
len++;
if (len == b[id]) {
id++;
len = 0;
if (id == m) {
return true;
}
}
} else {
len = 0;
}
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n,m;
cin >> n >> m;
vector<int> a(n), b(m);
int mx = 0;
for (int i = 0; i < n; i++){
cin >> a[i];
mx = max(mx, a[i]);
}
for(int i = 0; i < m; i++) {
cin >> b[i];
}
int l = 1, r = mx;
// 二分枚举
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid, a, b)) {
r = mid;
} else {
l = mid + 1;
}
}
cout << l << endl;
}
return 0;
}
Java
java
import java.io.*;
import java.util.*;
// 贪心线性检验不超过x情况能否全部分配
public class Main {
static boolean check(int x, int[] a, int[] b) {
int n = a.length;
int m = b.length;
int id = 0;
int len = 0;
for (int i = 0; i < n; i++) {
if (a[i] <= x) {
len++;
if (len == b[id]) {
id++;
len = 0;
if (id == m) {
return true;
}
}
} else {
len = 0;
}
}
return false;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
while (T-- > 0) {
int n = sc.nextInt();
int m = sc.nextInt();
int[] a = new int[n];
int[] b = new int[m];
int mx = 0;
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
mx = Math.max(mx, a[i]);
}
for (int i = 0; i < m; i++) {
b[i] = sc.nextInt();
}
int l = 1, r = mx;
// 二分枚举
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid, a, b)) {
r = mid;
} else {
l = mid + 1;
}
}
System.out.println(l);
}
}
}
python
python
# 贪心线性检验不超过x情况能否全部分配
def check(x, a, b):
n = len(a)
m = len(b)
id = 0
length = 0
for i in range(n):
if a[i] <= x:
length += 1
if length == b[id]:
id += 1
length = 0
if id == m:
return True
else:
length = 0
return False
T = int(input())
for _ in range(T):
n, m = map(int, input().split())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
mx = max(a)
l, r = 1, mx
# 二分枚举
while l < r:
mid = (l + r) >> 1
if check(mid, a, b):
r = mid
else:
l = mid + 1
print(l)
Javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const input = [];
rl.on('line', line => {
input.push(...line.trim().split(/\s+/));
});
rl.on('close', () => {
let pos = 0;
let T = Number(input[pos++]);
const ans = [];
// 贪心线性检验不超过x情况能否全部分配
function check(x, a, b) {
const n = a.length;
const m = b.length;
let id = 0;
let len = 0;
for (let i = 0; i < n; i++) {
if (a[i] <= x) {
len++;
if (len === b[id]) {
id++;
len = 0;
if (id === m) {
return true;
}
}
} else {
len = 0;
}
}
return false;
}
while (T-- > 0) {
const n = Number(input[pos++]);
const m = Number(input[pos++]);
const a = new Array(n);
const b = new Array(m);
let mx = 0;
for (let i = 0; i < n; i++) {
a[i] = Number(input[pos++]);
mx = Math.max(mx, a[i]);
}
for (let i = 0; i < m; i++) {
b[i] = Number(input[pos++]);
}
let l = 1;
let r = mx;
// 二分枚举
while (l < r) {
const mid = Math.floor((l + r) / 2);
if (check(mid, a, b)) {
r = mid;
} else {
l = mid + 1;
}
}
ans.push(l);
}
console.log(ans.join('\n'));
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
// 贪心线性检验不超过x情况能否全部分配
func check(x int, a []int, b []int) bool {
n := len(a)
m := len(b)
id := 0
length := 0
for i := 0; i < n; i++ {
if a[i] <= x {
length++
if length == b[id] {
id++
length = 0
if id == m {
return true
}
}
} else {
length = 0
}
}
return false
}
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, m int
fmt.Fscan(in, &n, &m)
a := make([]int, n)
b := make([]int, m)
mx := 0
for i := 0; i < n; i++ {
fmt.Fscan(in, &a[i])
if a[i] > mx {
mx = a[i]
}
}
for i := 0; i < m; i++ {
fmt.Fscan(in, &b[i])
}
l, r := 1, mx
// 二分枚举
for l < r {
mid := (l + r) >> 1
if check(mid, a, b) {
r = mid
} else {
l = mid + 1
}
}
fmt.Fprintln(out, l)
}
}