modify code

master
algorithmzuo 2 years ago
parent ad21237a2c
commit 2c8ee25e9b

@ -1,119 +0,0 @@
package 02.mca_02;
// 每周有营养的大厂算法面试题
// 课时205
// 有n个动物重量分别是a1、a2、a3.....an
// 这群动物一起玩叠罗汉游戏,
// 规定从左往右选择动物,每只动物左边动物的总重量不能超过自己的重量
// 返回最多能选多少个动物,求一个高效的算法。
// 比如有7个动物从左往右重量依次为135791121
// 则最多能选5个动物135921
// 注意本题给的例子是有序的,但是实际给定的动物数组,可能是无序的,
// 要求从左往右选动物,且不能打乱原始数组
public class Code01_MaxAnimalNumber {
// 普通动态规划
// 非常一般的方法
// 来自背包的思路
public static int maxAnimals1(int[] arr) {
int sum = 0;
for (int num : arr) {
sum += num;
}
int[][] dp = new int[arr.length][sum + 1];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j <= sum; j++) {
dp[i][j] = -1;
}
}
return process1(arr, 0, 0, dp);
}
public static int process1(int[] arr, int index, int pre, int[][] dp) {
if (index == arr.length) {
return 0;
}
if (dp[index][pre] != -1) {
return dp[index][pre];
}
int p1 = process1(arr, index + 1, pre, dp);
int p2 = 0;
if (arr[index] >= pre) {
p2 = 1 + process1(arr, index + 1, pre + arr[index], dp);
}
int ans = Math.max(p1, p2);
dp[index][pre] = ans;
return ans;
}
// 最优解
// 如果arr长度为N时间复杂度O(N*logN)
public static int maxAnimals2(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
// ends数组
int[] ends = new int[arr.length + 1];
ends[0] = 0;
int endsSize = 1;
int max = 1;
for (int i = 0; i < arr.length; i++) {
int l = 0;
int r = endsSize - 1;
int m = 0;
int find = 0;
while (l <= r) {
m = (l + r) / 2;
if (ends[m] <= arr[i]) {
find = m;
l = m + 1;
} else {
r = m - 1;
}
}
if (find == endsSize - 1) {
ends[endsSize] = ends[endsSize - 1] + arr[i];
endsSize++;
} else {
if (ends[find + 1] > ends[find] + arr[i]) {
ends[find + 1] = ends[find] + arr[i];
}
}
max = Math.max(max, find + 1);
}
return max;
}
public static int[] randomArray(int n, int v) {
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = (int) (Math.random() * v) + 1;
}
return arr;
}
public static void main(String[] args) {
int N = 100;
int V = 1000;
int testTime = 2000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int n = (int) (Math.random() * N) + 1;
int[] arr = randomArray(n, V);
int ans1 = maxAnimals1(arr);
int ans2 = maxAnimals2(arr);
if (ans1 != ans2) {
System.out.println("出错了");
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("测试结束");
}
}

@ -8,7 +8,7 @@ package 第02期.mca_02;
// 每个人在自己的回合一定要拿不超过3个球不能不拿
// 最终谁的总球数为偶数,谁赢
// 请问谁有必胜策略
public class Code06_WhoWin21Balls {
public class Code01_WhoWin21Balls {
// balls = 21
// ball是奇数

@ -0,0 +1,97 @@
package 02.mca_02;
import java.util.PriorityQueue;
// 每周有营养的大厂算法面试题
// 课时101~课时103
// 来自谷歌
// 给定一个数组arr长度为n
// 表示n个服务员每个人服务一个人的时间
// 给定一个正数m表示有m个人等位
// 如果你是刚来的人,请问你需要等多久?
// 假设m远远大于n比如n<=1000, m <= 10的9次方该怎么做
public class Code02_MinWaitingTime {
public static int minWaitingTime1(int[] arr, int m) {
if (arr == null || arr.length == 0) {
return -1;
}
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> (a[0] - b[0]));
int n = arr.length;
for (int i = 0; i < n; i++) {
heap.add(new int[] { 0, arr[i] });
}
for (int i = 0; i < m; i++) {
int[] cur = heap.poll();
cur[0] += cur[1];
heap.add(cur);
}
return heap.peek()[0];
}
public static int minWaitingTime2(int[] arr, int m) {
if (arr == null || arr.length == 0) {
return -1;
}
int best = Integer.MAX_VALUE;
for (int num : arr) {
best = Math.min(best, num);
}
int left = 0;
int right = best * m;
int mid = 0;
int near = 0;
while (left <= right) {
mid = (left + right) / 2;
int cover = 0;
for (int num : arr) {
cover += (mid / num) + 1;
}
if (cover >= m + 1) {
near = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return near;
}
// 为了测试
public static int[] randomArray(int n, int v) {
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = (int) (Math.random() * v) + 1;
}
return arr;
}
// 为了测试
public static void main(String[] args) {
int len = 50;
int value = 30;
int mMax = 3000;
int testTime = 20000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int n = (int) (Math.random() * len) + 1;
int[] arr = randomArray(n, value);
int m = (int) (Math.random() * mMax);
int ans1 = minWaitingTime1(arr, m);
int ans2 = minWaitingTime2(arr, m);
if (ans1 != ans2) {
System.out.println("出错了!");
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
System.out.println("m : " + m);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,96 @@
package 02.mca_02;
// 每周有营养的大厂算法面试题
// 课时111~课时112
// 来自美团
// 最大子段和是
// 一个经典问题,即对于一个数组找出其和最大的子数组。
// 现在允许你在求解该问题之前翻转这个数組的连续一段
// 如翻转(1,2,3,4,5,6)的第三个到第五个元素組成的子数组得到的是(1,2,5,4,3,6)
// 则翻转后该数组的最大子段和最大能达到多少?
public class Code03_MaxSumOnReverseArray {
public static int maxSumReverse1(int[] arr) {
int ans = Integer.MIN_VALUE;
for (int L = 0; L < arr.length; L++) {
for (int R = L; R < arr.length; R++) {
reverse(arr, L, R);
ans = Math.max(ans, maxSum(arr));
reverse(arr, L, R);
}
}
return ans;
}
public static void reverse(int[] arr, int L, int R) {
while (L < R) {
int tmp = arr[L];
arr[L++] = arr[R];
arr[R--] = tmp;
}
}
public static int maxSum(int[] arr) {
int pre = arr[0];
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
pre = Math.max(arr[i], arr[i] + pre);
max = Math.max(max, pre);
}
return max;
}
public static int maxSumReverse2(int[] arr) {
int n = arr.length;
int[] prefix = new int[n];
prefix[n - 1] = arr[n - 1];
for (int i = n - 2; i >= 0; i--) {
prefix[i] = arr[i] + Math.max(0, prefix[i + 1]);
}
int ans = prefix[0];
int suffix = arr[0];
int maxSuffix = suffix;
for (int i = 1; i < n; i++) {
ans = Math.max(ans, maxSuffix + prefix[i]);
suffix = arr[i] + Math.max(0, suffix);
maxSuffix = Math.max(maxSuffix, suffix);
}
ans = Math.max(ans, maxSuffix);
return ans;
}
// 为了测试
public static int[] randomArray(int n, int v) {
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = (int) (Math.random() * v) - (int) (Math.random() * v);
}
return arr;
}
// 为了测试
public static void main(String[] args) {
int len = 50;
int value = 20;
int testTime = 20000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int n = (int) (Math.random() * len) + 1;
int[] arr = randomArray(n, value);
int ans1 = maxSumReverse1(arr);
int ans2 = maxSumReverse2(arr);
if (ans1 != ans2) {
System.out.println("出错了!");
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("测试结束");
}
}

@ -1,157 +0,0 @@
package 02.mca_02;
import java.util.Arrays;
import java.util.PriorityQueue;
// 每周有营养的大厂算法面试题
// 课时213
// 在一个 n x n 的整数矩阵 grid 中,
// 每一个方格的值 grid[i][j] 表示位置 (i, j) 的平台高度。
// 当开始下雨时在时间为 t 水池中的水位为 t 
// 你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。
// 假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。
// 当然,在你游泳的时候你必须待在坐标方格里面。
// 你从坐标方格的左上平台 (00) 出发。
// 返回 你到达坐标方格的右下平台 (n-1, n-1) 所需的最少时间 。
// 测试链接 https://leetcode.cn/problems/swim-in-rising-water
public class Code03_SwimInRisingWater {
// 并查集的解法
public static int swimInWater1(int[][] grid) {
// 行号
int n = grid.length;
// 列号
int m = grid[0].length;
// [0,0,5]
// [0,1,3]....
int[][] points = new int[n * m][3];
int pi = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
points[pi][0] = i;
points[pi][1] = j;
points[pi++][2] = grid[i][j];
}
}
// 所有格子小对象,生成好了!
// 排序![a,b,c] [d,e,f]
Arrays.sort(points, (a, b) -> a[2] - b[2]);
// 生成并查集n * m
// 初始化的时候,把所有格子独自成一个集合!
UnionFind uf = new UnionFind(n, m);
int ans = 0;
for (int i = 0; i < points.length; i++) {
int r = points[i][0];
int c = points[i][1];
int v = points[i][2];
if (r > 0 && grid[r - 1][c] <= v) {
uf.union(r, c, r - 1, c);
}
if (r < n - 1 && grid[r + 1][c] <= v) {
uf.union(r, c, r + 1, c);
}
if (c > 0 && grid[r][c - 1] <= v) {
uf.union(r, c, r, c - 1);
}
if (c < m - 1 && grid[r][c + 1] <= v) {
uf.union(r, c, r, c + 1);
}
if (uf.isSameSet(0, 0, n - 1, m - 1)) {
ans = v;
break;
}
}
return ans;
}
public static class UnionFind {
public int col;
public int pointsSize;
public int[] father;
public int[] size;
public int[] help;
public UnionFind(int n, int m) {
col = m;
pointsSize = n * m;
father = new int[pointsSize];
size = new int[pointsSize];
help = new int[pointsSize];
for (int i = 0; i < pointsSize; i++) {
father[i] = i;
size[i] = 1;
}
}
private int find(int i) {
int hi = 0;
while (i != father[i]) {
help[hi++] = i;
i = father[i];
}
while (hi > 0) {
father[help[--hi]] = i;
}
return i;
}
private int index(int i, int j) {
return i * col + j;
}
public void union(int row1, int col1, int row2, int col2) {
int f1 = find(index(row1, col1));
int f2 = find(index(row2, col2));
if (f1 != f2) {
if (size[f1] >= size[f2]) {
father[f2] = f1;
size[f1] += size[f2];
} else {
father[f1] = f2;
size[f2] += size[f1];
}
}
}
public boolean isSameSet(int row1, int col1, int row2, int col2) {
return find(index(row1, col1)) == find(index(row2, col2));
}
}
// Dijkstra算法
public static int swimInWater2(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[2] - b[2]);
boolean[][] visited = new boolean[n][m];
heap.add(new int[] { 0, 0, grid[0][0] });
int ans = 0;
while (!heap.isEmpty()) {
int r = heap.peek()[0];
int c = heap.peek()[1];
int v = heap.peek()[2];
heap.poll();
if (visited[r][c]) {
continue;
}
visited[r][c] = true;
if (r == n - 1 && c == m - 1) {
ans = v;
break;
}
add(grid, heap, visited, r - 1, c, v);
add(grid, heap, visited, r + 1, c, v);
add(grid, heap, visited, r, c - 1, v);
add(grid, heap, visited, r, c + 1, v);
}
return ans;
}
public static void add(int[][] grid, PriorityQueue<int[]> heap, boolean[][] visited, int r, int c, int preV) {
if (r >= 0 && r < grid.length && c >= 0 && c < grid[0].length && !visited[r][c]) {
heap.add(new int[] { r, c, preV + Math.max(0, grid[r][c] - preV) });
}
}
}

@ -1,163 +0,0 @@
package 02.mca_02;
import java.util.ArrayList;
import java.util.Arrays;
// 每周有营养的大厂算法面试题
// 课时230
// 来自米哈游
// 给定一个正数n表示有多少个节点
// 给定一个二维数组edges表示所有无向边
// edges[i] = {a, b} 表示a到b有一条无向边
// edges一定表示的是一个无环无向图也就是树结构
// 每个节点可以染1、2、3三种颜色
// 要求 : 非叶节点的相邻点一定要至少有两种和自己不同颜色的点
// 返回一种达标的染色方案,也就是一个数组,表示每个节点的染色状况
// 1 <= 节点数量 <= 10的5次方
public class Code04_TreeDye {
// 1 2 3 1 2 3 1 2 3
public static int[] rule1 = { 1, 2, 3 };
// 1 3 2 1 3 2 1 3 2
public static int[] rule2 = { 1, 3, 2 };
public static int[] dye(int n, int[][] edges) {
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
// 0 : { 2, 1 }
// 1 : { 0 }
// 2 : { 0 }
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
for (int[] edge : edges) {
// 0 -> 2
// 1 -> 0
graph.get(edge[0]).add(edge[1]);
graph.get(edge[1]).add(edge[0]);
}
// 选一个头节点!
int head = -1;
for (int i = 0; i < n; i++) {
if (graph.get(i).size() >= 2) {
head = i;
break;
}
}
// graph
// head
int[] colors = new int[n];
if (head == -1) { // 两个点,互相连一下
// 把colors所有位置都设置成1
Arrays.fill(colors, 1);
} else {
// dfs 染色了!
colors[head] = 1;
dfs(graph, graph.get(head).get(0), 1, rule1, colors);
for (int i = 1; i < graph.get(head).size(); i++) {
dfs(graph, graph.get(head).get(i), 1, rule2, colors);
}
}
return colors;
}
// 整个图结构都在graph
// 当前来到的节点是head号节点
// head号节点在level层
// 染色的规则rule {1,2,3...} {1,3,2...}
// 做的事情以head为头的整颗树每个节点都染上颜色
// 填入到colors数组里去
public static void dfs(
ArrayList<ArrayList<Integer>> graph,
int head,
int level,
int[] rule, int[] colors) {
colors[head] = rule[level % 3];
for (int next : graph.get(head)) {
if (colors[next] == 0) {
dfs(graph, next, level + 1, rule, colors);
}
}
}
// 为了测试
// 生成无环无向图
public static int[][] randomEdges(int n) {
int[] order = new int[n];
for (int i = 0; i < n; i++) {
order[i] = i;
}
for (int i = n - 1; i >= 0; i--) {
swap(order, i, (int) (Math.random() * (i + 1)));
}
int[][] edges = new int[n - 1][2];
for (int i = 1; i < n; i++) {
edges[i - 1][0] = order[i];
edges[i - 1][1] = order[(int) (Math.random() * i)];
}
return edges;
}
// 为了测试
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
// 为了测试
public static boolean rightAnswer(int n, int[][] edges, int[] colors) {
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
for (int[] edge : edges) {
graph.get(edge[0]).add(edge[1]);
graph.get(edge[1]).add(edge[0]);
}
boolean[] hasColors = new boolean[4];
for (int i = 0, colorCnt = 1; i < n; i++, colorCnt = 1) {
if (colors[i] == 0) {
return false;
}
if (graph.get(i).size() <= 1) { // i号点是叶节点
continue;
}
hasColors[colors[i]] = true;
for (int near : graph.get(i)) {
if (!hasColors[colors[near]]) {
hasColors[colors[near]] = true;
colorCnt++;
}
}
if (colorCnt != 3) {
return false;
}
Arrays.fill(hasColors, false);
}
return true;
}
public static void main(String[] args) {
// int n = 7;
// int[][] edges = randomEdges(n);
// for (int[] edge : edges) {
// System.out.println(edge[0] + " , " + edge[1]);
// }
int N = 100;
int testTimes = 1000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int[][] edges = randomEdges(n);
int[] ans = dye(n, edges);
if (!rightAnswer(n, edges, ans)) {
System.out.println("出错了");
}
}
System.out.println("测试结束");
}
}

@ -16,7 +16,7 @@ package 第02期.mca_02;
// 防风带整体的防风高度为5是7、5、6中的最小值
// 给定一个正数kk <= matrix的行数表示可以取连续的k行这k行一起防风。
// 求防风带整体的防风高度最大值
public class Code02_WindPrevent {
public class Code04_WindPrevent {
public static int bestHeight1(int[][] matrix, int k) {
int n = matrix.length;

@ -1,121 +0,0 @@
package 02.mca_02;
// 每周有营养的大厂算法面试题
// 课时235
// 来自学员问题
// 给定怪兽的血量为hp
// 第i回合如果用刀砍怪兽在这回合会直接掉血没有后续效果
// 第i回合如果用毒怪兽在这回合不会掉血
// 但是之后每回合都会掉血,并且所有中毒的后续效果会叠加
// 给定的两个数组cuts、poisons两个数组等长长度都是n
// 表示你在n回合内的行动
// 每一回合的刀砍的效果由cuts[i]表示
// 每一回合的中毒的效果由poisons[i]表示
// 如果你在n个回合内没有直接杀死怪兽意味着你已经无法有新的行动了
// 但是怪兽如果有中毒效果的话那么怪兽依然会在hp耗尽的那回合死掉
// 返回你最快能在多少回合内将怪兽杀死
// 数据范围 :
// 1 <= n <= 10的5次方
// 1 <= hp <= 10的9次方
// 1 <= cuts[i]、poisons[i] <= 10的9次方
public class Code05_CutOrPoison {
// 不算好的方法
// 为了验证
public static int fast1(int[] cuts, int[] poisons, int hp) {
int sum = 0;
for (int num : poisons) {
sum += num;
}
int[][][] dp = new int[cuts.length][hp + 1][sum + 1];
return process1(cuts, poisons, 0, hp, 0, dp);
}
public static int process1(int[] cuts, int[] poisons, int index, int restHp, int poisonEffect, int[][][] dp) {
restHp -= poisonEffect;
if (restHp <= 0) {
return index + 1;
}
// restHp > 0
if (index == cuts.length) {
if (poisonEffect == 0) {
return Integer.MAX_VALUE;
} else {
return cuts.length + 1 + (restHp + poisonEffect - 1) / poisonEffect;
}
}
if (dp[index][restHp][poisonEffect] != 0) {
return dp[index][restHp][poisonEffect];
}
int p1 = restHp <= cuts[index] ? (index + 1)
: process1(cuts, poisons, index + 1, restHp - cuts[index], poisonEffect, dp);
int p2 = process1(cuts, poisons, index + 1, restHp, poisonEffect + poisons[index], dp);
int ans = Math.min(p1, p2);
dp[index][restHp][poisonEffect] = ans;
return ans;
}
// 真正想实现的方法
// O(N * log(hp))
public static int fast2(int[] cuts, int[] poisons, int hp) {
// 怪兽可能的最快死亡回合
int l = 1;
// 怪兽可能的最晚死亡回合
int r = hp + 1;
int m = 0;
int ans = Integer.MAX_VALUE;
while (l <= r) {
m = l + ((r - l) >> 1);
if (ok(cuts, poisons, hp, m)) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
public static boolean ok(int[] cuts, int[] posions, long hp, int limit) {
int n = Math.min(cuts.length, limit);
for (int i = 0, j = 1; i < n; i++, j++) {
hp -= Math.max((long) cuts[i], (long) (limit - j) * (long) posions[i]);
if (hp <= 0) {
return true;
}
}
return false;
}
// 为了测试
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * v) + 1;
}
return ans;
}
// 为了测试
public static void main(String[] args) {
int N = 30;
int cutV = 20;
int posionV = 10;
int hpV = 200;
int testTimes = 10000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int[] cuts = randomArray(n, cutV);
int[] posions = randomArray(n, posionV);
int hp = (int) (Math.random() * hpV) + 1;
int ans1 = fast1(cuts, posions, hp);
int ans2 = fast2(cuts, posions, hp);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}

@ -12,7 +12,7 @@ package 第02期.mca_02;
// 给定k次操作每一次操作用(a, b, c)表示在大立方体的该位置进行染色
// 返回长度为k的数组表示每一次操作后白色方块的连通块数
// n * m * h <= 10 ^ 5k <= 10 ^ 5
public class Code07_RedAndWhiteSquares {
public class Code05_RedAndWhiteSquares {
// 暴力方法
// 时间复杂度(k * n * m * h);

@ -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("测试线段树结束");
}
}
Loading…
Cancel
Save