测温共值最长段
拼多多 10月11号 笔试真题 第二题
拼多多真题目录点击查看: 拼多多笔试真题题库目录|笔试题库 + 算法考点详解
题目内容
机房按天记录服务器机柜温度。第 i i i 天的读数是一个闭区间 a i , b i a_i,b_i ai,bi,表示真实温度(取整数刻度)可能落在该区间内。共有 m m m 天记录,并按日期先后排列。
需要选出一段非空的连续日期 ,下标记为 p , q p,q p,q。在这段日期中,允许至多把一天的记录当作故障并丢弃 ,也可以一天都不丢。丢弃后至少还要留下一天记录,并且存在整数 t t t,落在每一条保留记录的闭区间内。换言之,未丢弃的那些天里,真实温度都有可能取同一个整数 t t t。
所选连续段的跨度定义为 q − p + 1 q-p+1 q−p+1;被丢弃的那一天仍然计入跨度。请给出合法跨度的最大值。
输入描述
首行给出整数 m m m( 1 ≤ m ≤ 2 × 10 5 1\le m\le 2\times 10^5 1≤m≤2×105),即记录天数。
第二行 m m m 个整数 a 1 , a 2 , ... , a m a_1,a_2,\dots,a_m a1,a2,...,am。
第三行 m m m 个整数 b 1 , b 2 , ... , b m b_1,b_2,\dots,b_m b1,b2,...,bm。
保证对每个 i i i 都有 1 ≤ a i ≤ b i ≤ 10 9 1\le a_i\le b_i\le 10^9 1≤ai≤bi≤109。本题仅一组数据。
输出描述
输出一个整数,即合法连续日期跨度的最大值。
样例1
输入
5
1 2 8 3 4
5 4 9 6 7
输出
5
说明
取全部 5 5 5 天,丢弃第 3 3 3 天的 8 , 9 8,9 8,9。其余四天都包含 4 4 4,长度为 5 5 5。
样例2
输入
6
1 2 3 4 5 6
2 4 5 6 7 8
输出
4
说明
例如取前 4 4 4 天并丢弃第 1 1 1 天,其余三天都包含 4 4 4。不存在长度为 5 5 5 的合法段。
样例3
输入
2
1 100
1 100
输出
2
说明
两天区间不相交,可丢弃其中任意一天,仍保留一天记录,故长度 2 2 2 合法。
题解
思路
解题算法:堆 + 滑动窗口
- 使用两个队维护,堆中的元素保存二元组
(值, 下标),方便判断元素是否已经离开滑动窗口。maxHeap: 维护区间内a,按照从大到小排列minHeap: 维护区间内b,按照从小到达排列
- 对于当前窗口
[l,r], 设- A1:窗口最大a,A2窗口内第二大a
- B1:窗口内最小b,B2窗口内第二小b
- 那么窗口的合法条件为
A2 <= B1 || A1 <= B2 || A1 <= B1,分别对应- 删除最大a
A2 <= B1 - 删除最小B
A1 <= B2 - 不删除记录
A1 <= B1
- 删除最大a
- 通过上述逻辑,移动窗口右边界,并计算以r结尾窗口的最小合法左边界l,即可当前合法窗口大小为
r - l + 1, 对应记录其中出现最大合法窗口长度即可。 - 算法总体时间复杂度为
O(mlogm)
C++
cpp
#include <bits/stdc++.h>
using namespace std;
struct Node {
int value, index;
bool operator<(const Node& other) const {
return value < other.value;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int m;
cin >> m;
vector<int> a(m), b(m);
for (int i = 0; i < m; i++) cin >> a[i];
for (int i = 0; i < m; i++) cin >> b[i];
// 大顶堆:按照 a 从大到小排列
priority_queue<Node> maxHeap;
// 小顶堆:按照 b 从小到大排列
priority_queue<Node, vector<Node>,
function<bool(const Node&, const Node&)>> minHeap(
[](const Node& x, const Node& y) {
return x.value > y.value;
}
);
int l = 0, ans = 1;
for (int r = 0; r < m; r++) {
// 将当前记录加入两个堆
maxHeap.push({a[r], r});
minHeap.push({b[r], r});
while (true) {
// 清理已经离开窗口的堆顶元素
while (!maxHeap.empty() && maxHeap.top().index < l) {
maxHeap.pop();
}
while (!minHeap.empty() && minHeap.top().index < l) {
minHeap.pop();
}
int len = r - l + 1;
if (len <= 1) break;
// 获取 a 的最大值和第二大值
Node firstA = maxHeap.top();
maxHeap.pop();
// 清理过期元素后,再获取第二大值
while (!maxHeap.empty() && maxHeap.top().index < l) {
maxHeap.pop();
}
Node secondA = maxHeap.top();
maxHeap.push(firstA);
// 获取 b 的最小值和第二小值
Node firstB = minHeap.top();
minHeap.pop();
// 清理过期元素后,再获取第二小值
while (!minHeap.empty() && minHeap.top().index < l) {
minHeap.pop();
}
Node secondB = minHeap.top();
minHeap.push(firstB);
// 至多删除一天后,剩余区间必须存在公共交集
if (secondA.value <= firstB.value ||
firstA.value <= secondB.value) {
break;
}
// 当前窗口不合法,缩小左边界
l++;
}
ans = max(ans, r - l + 1);
}
cout << ans << '\n';
return 0;
}
java
java
import java.util.*;
public class Main {
static class Node {
int value, index;
Node(int value, int index) {
this.value = value;
this.index = index;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int m = sc.nextInt();
int[] a = new int[m];
int[] b = new int[m];
for (int i = 0; i < m; i++) a[i] = sc.nextInt();
for (int i = 0; i < m; i++) b[i] = sc.nextInt();
// 大顶堆:按照 a 从大到小排列
PriorityQueue<Node> maxHeap = new PriorityQueue<>(
(x, y) -> Integer.compare(y.value, x.value)
);
// 小顶堆:按照 b 从小到大排列
PriorityQueue<Node> minHeap = new PriorityQueue<>(
(x, y) -> Integer.compare(x.value, y.value)
);
int l = 0, ans = 1;
for (int r = 0; r < m; r++) {
// 将当前记录加入两个堆
maxHeap.offer(new Node(a[r], r));
minHeap.offer(new Node(b[r], r));
while (true) {
// 清理已经离开窗口的堆顶元素
while (!maxHeap.isEmpty() && maxHeap.peek().index < l) {
maxHeap.poll();
}
while (!minHeap.isEmpty() && minHeap.peek().index < l) {
minHeap.poll();
}
int len = r - l + 1;
if (len <= 1) break;
// 获取 a 的最大值和第二大值
Node firstA = maxHeap.poll();
// 清理过期元素后,再获取第二大值
while (!maxHeap.isEmpty() && maxHeap.peek().index < l) {
maxHeap.poll();
}
Node secondA = maxHeap.peek();
maxHeap.offer(firstA);
// 获取 b 的最小值和第二小值
Node firstB = minHeap.poll();
// 清理过期元素后,再获取第二小值
while (!minHeap.isEmpty() && minHeap.peek().index < l) {
minHeap.poll();
}
Node secondB = minHeap.peek();
minHeap.offer(firstB);
// 至多删除一天后,剩余区间必须存在公共交集
if (secondA.value <= firstB.value ||
firstA.value <= secondB.value) {
break;
}
// 当前窗口不合法,缩小左边界
l++;
}
ans = Math.max(ans, r - l + 1);
}
System.out.println(ans);
sc.close();
}
}
python
python
import sys
import heapq
def main():
input = sys.stdin.readline
m = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
# 大顶堆:通过负数实现,按照 a 从大到小排列
maxHeap = []
# 小顶堆:按照 b 从小到大排列
minHeap = []
l = 0
ans = 1
for r in range(m):
# 将当前记录加入两个堆
heapq.heappush(maxHeap, (-a[r], r))
heapq.heappush(minHeap, (b[r], r))
while True:
# 清理已经离开窗口的堆顶元素
while maxHeap and maxHeap[0][1] < l:
heapq.heappop(maxHeap)
while minHeap and minHeap[0][1] < l:
heapq.heappop(minHeap)
length = r - l + 1
if length <= 1:
break
# 获取 a 的最大值和第二大值
firstA = heapq.heappop(maxHeap)
# 清理过期元素后,再获取第二大值
while maxHeap and maxHeap[0][1] < l:
heapq.heappop(maxHeap)
secondA = maxHeap[0]
heapq.heappush(maxHeap, firstA)
# 获取 b 的最小值和第二小值
firstB = heapq.heappop(minHeap)
# 清理过期元素后,再获取第二小值
while minHeap and minHeap[0][1] < l:
heapq.heappop(minHeap)
secondB = minHeap[0]
heapq.heappush(minHeap, firstB)
A1 = -firstA[0]
A2 = -secondA[0]
B1 = firstB[0]
B2 = secondB[0]
# 至多删除一天后,剩余区间必须存在公共交集
if A2 <= B1 or A1 <= B2:
break
# 当前窗口不合法,缩小左边界
l += 1
ans = max(ans, r - l + 1)
print(ans)
if __name__ == "__main__":
main()
Go
go
package main
import (
"bufio"
"container/heap"
"fmt"
"os"
)
type Node struct {
value int
index int
}
// 大顶堆:按照 a 从大到小排列
type MaxHeap []Node
func (h MaxHeap) Len() int { return len(h) }
func (h MaxHeap) Less(i, j int) bool { return h[i].value > h[j].value }
func (h MaxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MaxHeap) Push(x interface{}) {
*h = append(*h, x.(Node))
}
func (h *MaxHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
}
// 小顶堆:按照 b 从小到大排列
type MinHeap []Node
func (h MinHeap) Len() int { return len(h) }
func (h MinHeap) Less(i, j int) bool { return h[i].value < h[j].value }
func (h MinHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(x interface{}) {
*h = append(*h, x.(Node))
}
func (h *MinHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
}
func main() {
reader := bufio.NewReader(os.Stdin)
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
var m int
fmt.Fscan(reader, &m)
a := make([]int, m)
b := make([]int, m)
for i := 0; i < m; i++ {
fmt.Fscan(reader, &a[i])
}
for i := 0; i < m; i++ {
fmt.Fscan(reader, &b[i])
}
maxHeap := &MaxHeap{}
minHeap := &MinHeap{}
heap.Init(maxHeap)
heap.Init(minHeap)
l, ans := 0, 1
for r := 0; r < m; r++ {
// 将当前记录加入两个堆
heap.Push(maxHeap, Node{a[r], r})
heap.Push(minHeap, Node{b[r], r})
for {
// 清理已经离开窗口的堆顶元素
for maxHeap.Len() > 0 && (*maxHeap)[0].index < l {
heap.Pop(maxHeap)
}
for minHeap.Len() > 0 && (*minHeap)[0].index < l {
heap.Pop(minHeap)
}
length := r - l + 1
if length <= 1 {
break
}
// 获取 a 的最大值和第二大值
firstA := heap.Pop(maxHeap).(Node)
// 清理过期元素后,再获取第二大值
for maxHeap.Len() > 0 && (*maxHeap)[0].index < l {
heap.Pop(maxHeap)
}
secondA := (*maxHeap)[0]
heap.Push(maxHeap, firstA)
// 获取 b 的最小值和第二小值
firstB := heap.Pop(minHeap).(Node)
// 清理过期元素后,再获取第二小值
for minHeap.Len() > 0 && (*minHeap)[0].index < l {
heap.Pop(minHeap)
}
secondB := (*minHeap)[0]
heap.Push(minHeap, firstB)
// 至多删除一天后,剩余区间必须存在公共交集
if secondA.value <= firstB.value ||
firstA.value <= secondB.value {
break
}
// 当前窗口不合法,缩小左边界
l++
}
if r-l+1 > ans {
ans = r - l + 1
}
}
fmt.Fprintln(writer, ans)
}
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', () => {
const m = Number(lines[0]);
const a = lines[1].split(/\s+/).map(Number);
const b = lines[2].split(/\s+/).map(Number);
// 大顶堆:按照 a 从大到小排列
class MaxHeap {
constructor() {
this.heap = [];
}
push(node) {
const h = this.heap;
h.push(node);
let i = h.length - 1;
while (i > 0) {
const p = (i - 1) >> 1;
if (h[p].value >= h[i].value) break;
[h[p], h[i]] = [h[i], h[p]];
i = p;
}
}
pop() {
const h = this.heap;
const top = h[0];
const last = h.pop();
if (h.length > 0) {
h[0] = last;
let i = 0;
while (true) {
let best = i;
const left = i * 2 + 1;
const right = i * 2 + 2;
if (left < h.length &&
h[left].value > h[best].value) {
best = left;
}
if (right < h.length &&
h[right].value > h[best].value) {
best = right;
}
if (best === i) break;
[h[i], h[best]] = [h[best], h[i]];
i = best;
}
}
return top;
}
peek() {
return this.heap[0];
}
get size() {
return this.heap.length;
}
}
// 小顶堆:按照 b 从小到大排列
class MinHeap {
constructor() {
this.heap = [];
}
push(node) {
const h = this.heap;
h.push(node);
let i = h.length - 1;
while (i > 0) {
const p = (i - 1) >> 1;
if (h[p].value <= h[i].value) break;
[h[p], h[i]] = [h[i], h[p]];
i = p;
}
}
pop() {
const h = this.heap;
const top = h[0];
const last = h.pop();
if (h.length > 0) {
h[0] = last;
let i = 0;
while (true) {
let best = i;
const left = i * 2 + 1;
const right = i * 2 + 2;
if (left < h.length &&
h[left].value < h[best].value) {
best = left;
}
if (right < h.length &&
h[right].value < h[best].value) {
best = right;
}
if (best === i) break;
[h[i], h[best]] = [h[best], h[i]];
i = best;
}
}
return top;
}
peek() {
return this.heap[0];
}
get size() {
return this.heap.length;
}
}
const maxHeap = new MaxHeap();
const minHeap = new MinHeap();
let l = 0;
let ans = 1;
for (let r = 0; r < m; r++) {
// 将当前记录加入两个堆
maxHeap.push({ value: a[r], index: r });
minHeap.push({ value: b[r], index: r });
while (true) {
// 清理已经离开窗口的堆顶元素
while (maxHeap.size && maxHeap.peek().index < l) {
maxHeap.pop();
}
while (minHeap.size && minHeap.peek().index < l) {
minHeap.pop();
}
const len = r - l + 1;
if (len <= 1) break;
// 获取 a 的最大值和第二大值
const firstA = maxHeap.pop();
// 清理过期元素后,再获取第二大值
while (maxHeap.size && maxHeap.peek().index < l) {
maxHeap.pop();
}
const secondA = maxHeap.peek();
maxHeap.push(firstA);
// 获取 b 的最小值和第二小值
const firstB = minHeap.pop();
// 清理过期元素后,再获取第二小值
while (minHeap.size && minHeap.peek().index < l) {
minHeap.pop();
}
const secondB = minHeap.peek();
minHeap.push(firstB);
// 至多删除一天后,剩余区间必须存在公共交集
if (secondA.value <= firstB.value ||
firstA.value <= secondB.value) {
break;
}
// 当前窗口不合法,缩小左边界
l++;
}
ans = Math.max(ans, r - l + 1);
}
console.log(ans);
});