|
|
|
@ -0,0 +1,204 @@
|
|
|
|
|
package 第02期.mca_02;
|
|
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
|
|
|
|
// 每周有营养的大厂算法面试题
|
|
|
|
|
// 课时92~课时93
|
|
|
|
|
// 来自字节飞书团队
|
|
|
|
|
// 在字节跳动,大家都使用飞书的日历功能进行会议室的预订,遇到会议高峰时期,
|
|
|
|
|
// 会议室就可能不够用,现在请你实现一个算法,判断预订会议时是否有空的会议室可用。
|
|
|
|
|
// 为简化问题,这里忽略会议室的大小,认为所有的会议室都是等价的,
|
|
|
|
|
// 只要空闲就可以容纳任意的会议,并且:
|
|
|
|
|
// 1. 所有的会议预订都是当日预订当日的时段
|
|
|
|
|
// 2. 会议时段是一个左闭右开的时间区间,精确到分钟
|
|
|
|
|
// 3. 每个会议室刚开始都是空闲状态,同一时间一个会议室只能进行一场会议
|
|
|
|
|
// 4. 会议一旦预订成功就会按时进行
|
|
|
|
|
// 比如上午11点到中午12点的会议即[660, 720)
|
|
|
|
|
// 给定一个会议室总数m
|
|
|
|
|
// 一个预定事件由[a,b,c]代表 :
|
|
|
|
|
// a代表预定动作的发生时间,早来早得; b代表会议的召开时间; c代表会议的结束时间
|
|
|
|
|
// 给定一个n*3的二维数组,即可表示所有预定事件
|
|
|
|
|
// 返回一个长度为n的boolean类型的数组,表示每一个预定时间是否成功
|
|
|
|
|
public class Code06_MeetingCheck {
|
|
|
|
|
|
|
|
|
|
public static boolean[] reserveMeetings(int m, int[][] meetings) {
|
|
|
|
|
// 会议的总场次
|
|
|
|
|
int n = meetings.length;
|
|
|
|
|
// 开头时间,结尾时间
|
|
|
|
|
int[] ranks = new int[n << 1];
|
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
|
|
ranks[i] = meetings[i][1];
|
|
|
|
|
ranks[i + n] = meetings[i][2] - 1;
|
|
|
|
|
}
|
|
|
|
|
Arrays.sort(ranks);
|
|
|
|
|
// 0 : [6, 100, 200]
|
|
|
|
|
// 1 : [4, 30, 300]
|
|
|
|
|
// 30,1 100,2 200,3 300,4
|
|
|
|
|
// [0,6,2,3]
|
|
|
|
|
// [1,4,1,4]
|
|
|
|
|
//
|
|
|
|
|
// 0 T/F , 1, T/ 2,
|
|
|
|
|
|
|
|
|
|
// [1,4,1,4] [0,6,2,3] ....
|
|
|
|
|
int[][] reMeetings = new int[n][4];
|
|
|
|
|
int max = 0;
|
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
|
|
reMeetings[i][0] = i;
|
|
|
|
|
reMeetings[i][1] = meetings[i][0];
|
|
|
|
|
reMeetings[i][2] = rank(ranks, meetings[i][1]);
|
|
|
|
|
reMeetings[i][3] = rank(ranks, meetings[i][2] - 1);
|
|
|
|
|
max = Math.max(max, reMeetings[i][3]);
|
|
|
|
|
}
|
|
|
|
|
SegmentTree st = new SegmentTree(max);
|
|
|
|
|
Arrays.sort(reMeetings, (a, b) -> a[1] - b[1]);
|
|
|
|
|
boolean[] ans = new boolean[n];
|
|
|
|
|
for (int[] meeting : reMeetings) {
|
|
|
|
|
if (st.queryMax(meeting[2], meeting[3]) < m) {
|
|
|
|
|
ans[meeting[0]] = true;
|
|
|
|
|
st.add(meeting[2], meeting[3], 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回>=num, 最左位置
|
|
|
|
|
public static int rank(int[] sorted, int num) {
|
|
|
|
|
int l = 0;
|
|
|
|
|
int r = sorted.length - 1;
|
|
|
|
|
int m = 0;
|
|
|
|
|
int ans = 0;
|
|
|
|
|
while (l <= r) {
|
|
|
|
|
m = (l + r) / 2;
|
|
|
|
|
if (sorted[m] >= num) {
|
|
|
|
|
ans = m;
|
|
|
|
|
r = m - 1;
|
|
|
|
|
} else {
|
|
|
|
|
l = m + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ans + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class SegmentTree {
|
|
|
|
|
private int n;
|
|
|
|
|
private int[] max;
|
|
|
|
|
private int[] lazy;
|
|
|
|
|
|
|
|
|
|
public SegmentTree(int maxSize) {
|
|
|
|
|
n = maxSize;
|
|
|
|
|
max = new int[n << 2];
|
|
|
|
|
lazy = new int[n << 2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void pushUp(int rt) {
|
|
|
|
|
max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void pushDown(int rt, int ln, int rn) {
|
|
|
|
|
if (lazy[rt] != 0) {
|
|
|
|
|
lazy[rt << 1] += lazy[rt];
|
|
|
|
|
max[rt << 1] += lazy[rt];
|
|
|
|
|
lazy[rt << 1 | 1] += lazy[rt];
|
|
|
|
|
max[rt << 1 | 1] += lazy[rt];
|
|
|
|
|
lazy[rt] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void add(int L, int R, int C) {
|
|
|
|
|
add(L, R, C, 1, n, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void add(int L, int R, int C, int l, int r, int rt) {
|
|
|
|
|
if (L <= l && r <= R) {
|
|
|
|
|
max[rt] += C;
|
|
|
|
|
lazy[rt] += C;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
int mid = (l + r) >> 1;
|
|
|
|
|
pushDown(rt, mid - l + 1, r - mid);
|
|
|
|
|
if (L <= mid) {
|
|
|
|
|
add(L, R, C, l, mid, rt << 1);
|
|
|
|
|
}
|
|
|
|
|
if (R > mid) {
|
|
|
|
|
add(L, R, C, mid + 1, r, rt << 1 | 1);
|
|
|
|
|
}
|
|
|
|
|
pushUp(rt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int queryMax(int L, int R) {
|
|
|
|
|
return queryMax(L, R, 1, n, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int queryMax(int L, int R, int l, int r, int rt) {
|
|
|
|
|
if (L <= l && r <= R) {
|
|
|
|
|
return max[rt];
|
|
|
|
|
}
|
|
|
|
|
int mid = (l + r) >> 1;
|
|
|
|
|
pushDown(rt, mid - l + 1, r - mid);
|
|
|
|
|
int ans = 0;
|
|
|
|
|
if (L <= mid) {
|
|
|
|
|
ans = Math.max(ans, queryMax(L, R, l, mid, rt << 1));
|
|
|
|
|
}
|
|
|
|
|
if (R > mid) {
|
|
|
|
|
ans = Math.max(ans, queryMax(L, R, mid + 1, r, rt << 1 | 1));
|
|
|
|
|
}
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 为了测试线段树
|
|
|
|
|
public static class Right {
|
|
|
|
|
public int[] arr;
|
|
|
|
|
|
|
|
|
|
public Right(int maxSize) {
|
|
|
|
|
arr = new int[maxSize + 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void add(int L, int R, int C) {
|
|
|
|
|
for (int i = L; i <= R; i++) {
|
|
|
|
|
arr[i] += C;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int queryMax(int L, int R) {
|
|
|
|
|
int ans = 0;
|
|
|
|
|
for (int i = L; i <= R; i++) {
|
|
|
|
|
ans = Math.max(ans, arr[i]);
|
|
|
|
|
}
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 测试线段树的对数器
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
int N = 50;
|
|
|
|
|
int V = 10;
|
|
|
|
|
int testTimes1 = 1000;
|
|
|
|
|
int testTimes2 = 1000;
|
|
|
|
|
System.out.println("测试线段树开始");
|
|
|
|
|
for (int i = 0; i < testTimes1; i++) {
|
|
|
|
|
int n = (int) (Math.random() * N) + 1;
|
|
|
|
|
SegmentTree st = new SegmentTree(n);
|
|
|
|
|
Right right = new Right(n);
|
|
|
|
|
for (int j = 0; j < testTimes2; j++) {
|
|
|
|
|
int a = (int) (Math.random() * n) + 1;
|
|
|
|
|
int b = (int) (Math.random() * n) + 1;
|
|
|
|
|
int L = Math.min(a, b);
|
|
|
|
|
int R = Math.max(a, b);
|
|
|
|
|
if (Math.random() < 0.5) {
|
|
|
|
|
int C = (int) (Math.random() * V);
|
|
|
|
|
st.add(L, R, C);
|
|
|
|
|
right.add(L, R, C);
|
|
|
|
|
} else {
|
|
|
|
|
if (st.queryMax(L, R) != right.queryMax(L, R)) {
|
|
|
|
|
System.out.println("出错了!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
System.out.println("测试线段树结束");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|