题目描述
公园园区提供小火车单向通行,从园区站点编号最小到最大通行如1~2~3~4~1,然后供员工在各个办公园区穿梭,通过对公司N个员工调研统计到每个员工的坐车区间,包含前后站点,请设计一个程序计算出小火车在哪个园区站点时人数最多。
输入描述
第1个行,为调研员工人数
第2行开始,为每个员工的上车站点和下车站点。
使用数字代替每个园区用空格分割,如3 5表示从第3个园区上车,在第5个园区下车
输出描述
人数最多时的园区站点编号,最多人数相同时返回编号最小的园区站点
用例
输入 | 3 1 3 2 4 1 4 |
输出 | 2 |
说明 | 无 |
题目解析
本题其实就是求解最大重叠区间个数的变种题。
即,我们只要找到具有最大重叠部分的区间的起点就是本题题解。
关于最大重叠区间个数求解,请看
上面视频的核心思想其实就是:
- 首先,将所有区间按开始位置升序
- 然后,遍历排序后区间,并将小顶堆中小于遍历区间起始位置的区间弹出(小顶堆实际存储区间结束位置),此操作后,小顶堆中剩余的区间个数,就是和当前遍历区间重叠数。
我们只需要在求解最大重叠数时,保留遍历的区间的起始位置即可。
对于JS而言,没有原生堆结构(即优先队列),因此我们需要手写小顶堆代码,关于优先队列,大家可以参考:
LeetCode – 1705 吃苹果的最大数目_伏城之外的博客-CSDN博客
但是本题是100分值的,可能不会存在大数量级,因此大家可以先尝试用有序数组代替小顶堆,如果不行,再实现小顶堆。
另外,本题和华为OD机试 – 最大化控制资源成本_伏城之外的博客-CSDN博客
很像,大家可以继续尝试做下上面这题。
2023.2.1根据网友指正,本题小火车是有可能走回程的,比如,坐车区间 为 [3,2],即从站点3 -> 站点4 -> 站点1 -> 站点2。
对于这种情况,就会破坏上面对于区间的排序。因此,我们需要将:该情况的坐车区间拆分为 [3, 4]和[1, 2]
JavaScript算法源码
/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const lines = [];
let n;
rl.on("line", (line) => {
lines.push(line);
if (lines.length === 1) {
n = lines[0] - 0;
}
if (n && lines.length === n + 1) {
const ranges = lines.slice(1).map((line) => line.split(" ").map(Number));
console.log(getResult(ranges));
lines.length = 0;
}
});
function getResult(ranges) {
// 由于题目并未说有几个站点,因此需要计算出最后一个站点
const last = Math.max.apply(null, ranges.toString().split(",").map(Number));
// 如果存在 [3,2] 这种坐车区间,即从站点3 -> 站点4 -> 站点1 -> 站点2,则此时将该坐车区间拆分为 [3, 4]和[1, 2]
const tmp = [];
ranges.forEach((range) => {
const [s, e] = range;
if (s > e) {
// 拆分
tmp.push([s, last]);
tmp.push([1, e]);
} else {
tmp.push(range);
}
});
ranges = tmp;
// 最大重叠区间个数求解
ranges.sort((a, b) => a[0] - b[0]);
const end = new PriorityQueue((a, b) => b - a);
let max = 0;
let ans = ranges[0][0];
for (let range of ranges) {
const [s, e] = range;
while (end.size()) {
const top = end.peek();
if (top < s) {
end.shift();
} else {
break;
}
}
end.push(e);
if (end.size() > max) {
max = end.size();
ans = s;
}
}
return ans;
}
class PriorityQueue {
constructor(cpr) {
this.queue = [];
this.cpr = cpr;
}
swap(a, b) {
const tmp = this.queue[a];
this.queue[a] = this.queue[b];
this.queue[b] = tmp;
}
// 上浮
swim() {
let c = this.queue.length - 1;
while (c >= 1) {
const f = Math.floor((c - 1) / 2);
if (this.cpr(this.queue[c], this.queue[f]) > 0) {
this.swap(c, f);
c = f;
} else {
break;
}
}
}
// 入队
push(val) {
this.queue.push(val);
this.swim();
}
// 下沉
sink() {
let f = 0;
while (true) {
let c1 = 2 * f + 1;
let c2 = c1 + 1;
let c;
let val1 = this.queue[c1];
let val2 = this.queue[c2];
if (val1 && val2) {
c = this.cpr(val1, val2) > 0 ? c1 : c2;
} else if (val1 && !val2) {
c = c1;
} else if (!val1 && val2) {
c = c2;
} else {
break;
}
if (this.cpr(this.queue[c], this.queue[f]) > 0) {
this.swap(c, f);
f = c;
} else {
break;
}
}
}
// 出队
shift() {
this.swap(0, this.queue.length - 1);
const res = this.queue.pop();
this.sink();
return res;
}
// 查看顶
peek() {
return this.queue[0];
}
size() {
return this.queue.length;
}
}
Java算法源码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] ranges = new int[n][2];
for (int i = 0; i < n; i++) {
ranges[i][0] = sc.nextInt();
ranges[i][1] = sc.nextInt();
}
System.out.println(getResult(ranges));
}
public static int getResult(int[][] ranges) {
// 由于题目并未说有几个站点,因此需要计算出最后一个站点
Integer last =
Arrays.stream(ranges)
.map(range -> Math.max(range[0], range[1]))
.max((a, b) -> a - b)
.orElse(0);
// 如果存在 [3,2] 这种坐车区间,即从站点3 -> 站点4 -> 站点1 -> 站点2,则此时将该坐车区间拆分为 [3, 4]和[1, 2]
ArrayList<Integer[]> tmp = new ArrayList<>();
for (int[] range : ranges) {
int s = range[0];
int e = range[1];
if (s > e) {
tmp.add(new Integer[] {s, last});
tmp.add(new Integer[] {1, e});
} else {
tmp.add(new Integer[] {s, e});
}
}
// 最大重叠区间个数求解
tmp.sort((a, b) -> a[0] - b[0]);
PriorityQueue<Integer> end = new PriorityQueue<>((a, b) -> a - b);
int max = 0;
int ans = tmp.get(0)[0];
for (Integer[] range : tmp) {
int s = range[0];
int e = range[1];
while (end.size() > 0) {
Integer top = end.peek();
if (top < s) {
end.poll();
} else {
break;
}
}
end.offer(e);
if (end.size() > max) {
max = end.size();
ans = s;
}
}
return ans;
}
}
Python算法源码
import queue
# 输入获取
n = int(input())
ranges = [list(map(int, input().split())) for i in range(n)]
# 算法入口
def getResult(ranges):
# 由于题目并未说有几个站点,因此需要计算出最后一个站点
last = max(map(lambda x: max(x[0], x[1]), ranges))
# 如果存在 [3,2] 这种坐车区间,即从站点3 -> 站点4 -> 站点1 -> 站点2,则此时将该坐车区间拆分为 [3, 4]和[1, 2]
tmp = []
for ran in ranges:
s, e = ran
if s > e:
tmp.append([s, last])
tmp.append([1, e])
else:
tmp.append(ran)
ranges = tmp
# 最大重叠区间个数求解
ranges.sort(key=lambda x: x[0])
end = queue.PriorityQueue()
maxV = 0
ans = ranges[0][0]
for ran in ranges:
s, e = ran
while end.qsize() > 0:
top = end.queue[0]
if top < s:
end.get()
else:
break
end.put(e)
if end.qsize() > maxV:
maxV = end.qsize()
ans = s
return ans
print(getResult(ranges))
基于差分数列求解本题(更适合本题)
关于差分数列,请看
算法设计 – 前缀和 & 差分数列_伏城之外的博客-CSDN博客
然后可以尝试下leetcode下面差分数列题目
LeetCode – 1109 – 航班预定统计_伏城之外的博客-CSDN博客
本题差分数列求解的解析可以参照上面两个博客
JavaScript算法源码
/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const lines = [];
let n;
rl.on("line", (line) => {
lines.push(line);
if (lines.length === 1) {
n = lines[0] - 0;
}
if (n && lines.length === n + 1) {
const ranges = lines.slice(1).map((line) => line.split(" ").map(Number));
console.log(getResult(ranges));
lines.length = 0;
}
});
function getResult(ranges) {
// 由于题目并未说有几个站点,因此需要计算出最后一个站点
const last = Math.max.apply(null, ranges.toString().split(",").map(Number));
const diff = new Array(last).fill(0);
for (let range of ranges) {
const [l, r] = range;
if (l > r) {
diff[l - 1] += 1;
diff[0] += 1;
diff[r] -= 1;
} else {
diff[l - 1] += 1;
if (r < last) diff[r] -= 1;
}
}
const preSum = [diff[0]];
let max = preSum[0];
let maxI = 0;
for (let i = 1; i < last; i++) {
preSum[i] = preSum[i - 1] + diff[i];
if (preSum[i] > max) {
max = preSum[i];
maxI = i;
}
}
return maxI + 1;
}
Java算法源码
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] ranges = new int[n][2];
for (int i = 0; i < n; i++) {
ranges[i][0] = sc.nextInt();
ranges[i][1] = sc.nextInt();
}
System.out.println(getResult(ranges));
}
public static int getResult(int[][] ranges) {
// 由于题目并未说有几个站点,因此需要计算出最后一个站点
int last =
Arrays.stream(ranges)
.map(range -> Math.max(range[0], range[1]))
.max((a, b) -> a - b)
.orElse(0);
int[] diff = new int[last];
for (int[] range : ranges) {
int l = range[0];
int r = range[1];
if (l > r) {
diff[l - 1] += 1;
diff[0] += 1;
diff[r] -= 1;
} else {
diff[l - 1] += 1;
if (r < last) diff[r] -= 1;
}
}
int[] preSum = new int[last];
int max = preSum[0] = diff[0];
int maxI = 0;
for (int i = 1; i < last; i++) {
preSum[i] = preSum[i - 1] + diff[i];
if (preSum[i] > max) {
max = preSum[i];
maxI = i;
}
}
return maxI + 1;
}
}
Python算法源码
# 输入获取
n = int(input())
ranges = [list(map(int, input().split())) for i in range(n)]
# 算法入口
def getResult(ranges):
# 由于题目并未说有几个站点,因此需要计算出最后一个站点
last = max(map(lambda x: max(x[0], x[1]), ranges))
diff = [0 for i in range(last)]
for ran in ranges:
l, r = ran
if l > r:
diff[l - 1] += 1
diff[0] += 1
diff[r] -= 1
else:
diff[l - 1] += 1
if r < last:
diff[r] -= 1
preSum = [0 for i in range(last)]
maxV = preSum[0] = diff[0]
maxI = 0
for i in range(1, last):
preSum[i] = preSum[i - 1] + diff[i]
if preSum[i] > maxV:
maxV = preSum[i]
maxI = i
return maxI + 1
# 算法调用
print(getResult(ranges))
免责声明:
评论0