modify code

master
algorithmzuo 3 years ago
parent a6bee58963
commit fb1da4a0a3

@ -0,0 +1,157 @@
package class_2022_09_1_week;
// 给你一个由小写字母组成的字符串 s ,和一个整数 k
// 如果满足下述条件,则可以将字符串 t 视作是 理想字符串
// t 是字符串 s 的一个子序列。
// t 中每两个 相邻 字母在字母表中位次的绝对差值小于或等于 k 。
// 返回 最长 理想字符串的长度。
// 字符串的子序列同样是一个字符串,并且子序列还满足:
// 可以经由其他字符串删除某些字符(也可以不删除)但不改变剩余字符的顺序得到。
// 注意:字母表顺序不会循环
// 例如,'a' 和 'z' 在字母表中位次的绝对差值是 25而不是 1 。
// 测试链接 : https://leetcode.cn/problems/longest-ideal-subsequence/
public class Code01_LongestIdealString {
// 二维动态规划的解
// N为字符串长度E为字符集大小K为差值要求
// 时间复杂度O(N*E)
// 空间复杂度O(N*E)
public static int longestIdealString1(String s, int k) {
int n = s.length();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = s.charAt(i) - 'a';
}
int[][] dp = new int[n][27];
for (int i = 0; i < n; i++) {
for (int j = 0; j <= 26; j++) {
dp[i][j] = -1; // -1代表没算过
}
}
return f(arr, 0, 26, k, dp);
}
// 数组s中所有的值都在0~25对应a~z
// 当前在s[i...]选择数字, 并且前一个数字是p
// 如果p<26说明选择的前一个数字是p
// 如果p==26说明之前没有选过任何数字
// 返回在前一个数字是p的情况下在s[i...]上选择数字,最长理想子序列能是多长
// dp仅仅是缓存结构暴力递归改动态规划常规技巧
public static int f(int[] s, int i, int p, int k, int[][] dp) {
if (i == s.length) {
return 0;
}
if (dp[i][p] != -1) {
return dp[i][p];
}
int p1 = f(s, i + 1, p, k, dp);
int p2 = 0;
if (p == 26 || Math.abs(s[i] - p) <= k) {
p2 = 1 + f(s, i + 1, s[i], k, dp);
}
int ans = Math.max(p1, p2);
dp[i][p] = ans;
return ans;
}
// 一维动态规划从左往右递推版
// N为字符串长度E为字符集大小K为差值要求
// 时间复杂度O(N*K)
// 空间复杂度O(E)
public static int longestIdealString2(String s, int k) {
int[] dp = new int[26];
int c, l, r, pre, ans = 0;
for (int i = 0; i < s.length(); i++) {
c = s.charAt(i) - 'a';
l = Math.max(c - k, 0);
r = Math.min(c + k, 25);
pre = 0;
for (int j = l; j <= r; j++) {
pre = Math.max(pre, dp[j]);
}
dp[c] = 1 + pre;
ans = Math.max(ans, dp[c]);
}
return ans;
}
// 从左往右递推 + 线段树优化
// N为字符串长度E为字符集大小K为差值要求
// 时间复杂度O(N * logE)
// 空间复杂度O(E)
public static int longestIdealString3(String s, int k) {
// 0 0 0
// 1(a) 2(b) ... 26(z)
SegmentTree st = new SegmentTree(26);
int c, pre, ans = 0;
for (int i = 0; i < s.length(); i++) {
// i s.charAt(i)
// a 1
// b 2
// z 26
c = s.charAt(i) - 'a' + 1;
// 2 k = 3
// 1 2 3 4 5 6 7
// l = Math.max(c - k, 1)
// r = Math.min(c + k, 26)
pre = st.max(Math.max(c - k, 1), Math.min(c + k, 26));
ans = Math.max(ans, 1 + pre);
st.update(c, 1 + pre);
}
return ans;
}
public static class SegmentTree {
private int n;
private int[] max;
public SegmentTree(int maxSize) {
n = maxSize + 1;
max = new int[n << 2];
}
public void update(int index, int c) {
update(index, index, c, 1, n, 1);
}
public int max(int left, int right) {
return max(left, right, 1, n, 1);
}
private void pushUp(int rt) {
max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]);
}
private void update(int L, int R, int C, int l, int r, int rt) {
if (L <= l && r <= R) {
max[rt] = C;
return;
}
int mid = (l + r) >> 1;
if (L <= mid) {
update(L, R, C, l, mid, rt << 1);
}
if (R > mid) {
update(L, R, C, mid + 1, r, rt << 1 | 1);
}
pushUp(rt);
}
private int max(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
return max[rt];
}
int mid = (l + r) >> 1;
int ans = 0;
if (L <= mid) {
ans = Math.max(ans, max(L, R, l, mid, rt << 1));
}
if (R > mid) {
ans = Math.max(ans, max(L, R, mid + 1, r, rt << 1 | 1));
}
return ans;
}
}
}

@ -0,0 +1,197 @@
package class_2022_09_1_week;
import java.util.Arrays;
// 来自美团
// 有n个城市城市从0到n-1进行编号。小美最初住在k号城市中
// 在接下来的m天里小美每天会收到一个任务
// 她可以选择完成当天的任务或者放弃该任务
// 第i天的任务需要在ci号城市完成如果她选择完成这个任务
// 若任务开始前她恰好在ci号城市则会获得ai的收益
// 若她不在ci号城市她会前往ci号城市获得bi的收益
// 当天的任务她都会当天完成
// 任务完成后她会留在该任务所在的ci号城市直到接受下一个任务
// 如果她选择放弃任务,她会停留原地,且不会获得收益
// 小美想知道,如果她合理地完成任务,最大能获得多少收益
// 输入描述: 第一行三个正整数n, m和k表示城市数量总天数初始所在城市
// 第二行为m个整数c1, c2,...... cm其中ci表示第i天的任务所在地点为ci
// 第三行为m个整数a1, a2,...... am其中ai表示完成第i天任务且地点不变的收益
// 第四行为m个整数b1, b2,...... bm其中bi表示完成第i天的任务且地点改变的收益
// 0 <= k, ci <= n <= 30000
// 1 <= m <= 30000
// 0 <= ai, bi <= 10^9
// 输出描述 输出一个整数,表示小美合理完成任务能得到的最大收益
public class Code02_MoveCityGetMoney {
// 暴力方法
// 时间复杂度O(N^2)
// 为了验证
public static int maxPorfit1(int n, int m, int k, int[] c, int[] a, int[] b) {
int[][] dp = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
dp[i][j] = -1;
}
}
return process1(k, 0, m, c, a, b, dp);
}
// cur : 小美当前在哪!
// i : 当前面临的是任务编号!
// m : 一共有多少任务,固定
// c[i] : 第i号任务要在哪个城里完成
// a[i] : 恰好在!收益
// b[i] : 赶过去!收益
// 返回 : i....... 小美获得的最大收益
public static int process1(int cur, int i, int m, int[] c, int[] a, int[] b, int[][] dp) {
if (i == m) {
return 0;
}
if (dp[cur][i] != -1) {
return dp[cur][i];
}
// 可能性1 : 不做任务,彻底放弃,留在原地
int p1 = process1(cur, i + 1, m, c, a, b, dp);
// 可能性2 : 做任务要看看cur在哪来获得收益
int p2 = 0;
if (cur == c[i]) {
p2 = a[i] + process1(c[i], i + 1, m, c, a, b, dp);
} else {
p2 = b[i] + process1(c[i], i + 1, m, c, a, b, dp);
}
int ans = Math.max(p1, p2);
dp[cur][i] = ans;
return ans;
}
// 正式方法
// 时间复杂度O(N*logN)
public static int maxPorfit2(int n, int m, int k, int[] c, int[] a, int[] b) {
SegmentTree st = new SegmentTree(n);
st.update(k, 0);
int ans = 0;
for (int i = 0; i < m; i++) {
// c[i]
int curAns = Math.max(Math.max(st.max(0, c[i] - 1), st.max(c[i] + 1, n - 1)) + b[i],
st.max(c[i], c[i]) + a[i]);
ans = Math.max(ans, curAns);
st.update(c[i], curAns);
}
return ans;
}
public static class SegmentTree {
private int n;
private int[] max;
public SegmentTree(int N) {
n = N;
max = new int[(n + 1) << 2];
Arrays.fill(max, Integer.MIN_VALUE);
}
public int max(int l, int r) {
l++;
r++;
if (l > r) {
return Integer.MIN_VALUE;
}
return max(l, r, 1, n, 1);
}
public void update(int i, int v) {
i++;
update(i, i, v, 1, n, 1);
}
private void pushUp(int rt) {
max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]);
}
private void update(int L, int R, int C, int l, int r, int rt) {
if (L <= l && r <= R) {
max[rt] = Math.max(max[rt], C);
return;
}
int mid = (l + r) >> 1;
if (L <= mid) {
update(L, R, C, l, mid, rt << 1);
}
if (R > mid) {
update(L, R, C, mid + 1, r, rt << 1 | 1);
}
pushUp(rt);
}
private int max(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
return max[rt];
}
int mid = (l + r) >> 1;
int left = Integer.MIN_VALUE;
int right = Integer.MIN_VALUE;
if (L <= mid) {
left = max(L, R, l, mid, rt << 1);
}
if (R > mid) {
right = max(L, R, mid + 1, r, rt << 1 | 1);
}
return Math.max(left, right);
}
}
// 为了测试
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);
}
return ans;
}
// 为了测试
public static void main(String[] args) {
int N = 100;
int M = 100;
int V = 10000;
int testTimes = 5000;
System.out.println("功能测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int m = (int) (Math.random() * M) + 1;
int k = (int) (Math.random() * n);
int[] c = randomArray(m, n);
int[] a = randomArray(m, V);
int[] b = randomArray(m, V);
int ans1 = maxPorfit1(n, m, k, c, a, b);
int ans2 = maxPorfit2(n, m, k, c, a, b);
if (ans1 != ans2) {
System.out.println("出错了!");
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
int n = 100000;
int m = 100000;
int v = 1000000;
int k = (int) (Math.random() * n);
int[] c = randomArray(m, n);
int[] a = randomArray(m, v);
int[] b = randomArray(m, v);
System.out.println("城市数量 : " + n);
System.out.println("任务天数 : " + m);
System.out.println("收益数值规模 : " + v);
long start = System.currentTimeMillis();
maxPorfit2(n, m, k, c, a, b);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + "毫秒");
System.out.println("性能测试结束");
}
}

@ -0,0 +1,307 @@
package class_2022_09_1_week;
import java.util.Arrays;
// 来自美团
// 给定一个正数n, 表示从0位置到n-1位置每个位置放着1件衣服
// 从0位置到n-1位置不仅有衣服每个位置还摆着1个机器人
// 给定两个长度为n的数组powers和rates
// powers[i]表示i位置的机器人的启动电量
// rates[i]表示i位置的机器人收起1件衣服的时间
// 使用每个机器人只需要付出启动电量
// 当i位置的机器人收起i位置的衣服它会继续尝试往右收起i+1位置衣服
// 如果i+1位置的衣服已经被其他机器人收了或者其他机器人正在收
// 这个机器人就会停机, 不再收衣服。
// 不过如果它不停机它会同样以rates[i]的时间来收起这件i+1位置的衣服
// 也就是收衣服的时间为每个机器人的固定属性当它收起i+1位置的衣服
// 它会继续检查i+2位置...一直到它停机或者右边没有衣服可以收了
// 形象的来说机器人会一直尝试往右边收衣服收k件的话就耗费k * rates[i]的时间
// 但是当它遇见其他机器人工作的痕迹,就会认为后面的事情它不用管了,进入停机状态
// 你手里总共有电量b准备在0时刻将所有想启动的机器人全部一起启动
// 过后不再启动新的机器人并且启动机器人的电量之和不能大于b
// 返回在最佳选择下,假快多久能收完所有衣服
// 如果无论如何都收不完所有衣服,返回-1
// 给定数据: int n, int b, int[] powers, int[] rates
// 数据范围:
// powers长度 == rates长度 == n <= 1000
// 1 <= b <= 10^5
// 1 <= powers[i]、rates[i] <= 10^5
// 0号 : 10^5 * 10^3 -> 10^8
// log 10^8 * N^2 -> 27 * 10^6 -> 10^7
// 优化之后 : (log10^8) -> 27 * 1000 * 10
public class Code03_RobotAndClothes {
// 通过不了的简单动态规划方法
// 只是为了对数器验证
public static int fast1(int n, int b, int[] powers, int[] rates) {
int[][] dp = new int[n][b + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j <= b; j++) {
dp[i][j] = -1;
}
}
int ans = process1(powers, rates, n, 0, b, dp);
return ans == Integer.MAX_VALUE ? -1 : ans;
}
// i....这些衣服
// 由i....这些机器人负责
// 在剩余电量还有rest的情况下
// 收完i....这些衣服最少时间是多少
// 如果怎么都收不完
// 返回Integer.MAX_VALUE
public static int process1(int[] powers, int[] rates, int n, int i, int rest, int[][] dp) {
if (i == n) {
return 0;
}
if (powers[i] > rest) {
return Integer.MAX_VALUE;
}
if (dp[i][rest] != -1) {
return dp[i][rest];
}
int ans = Integer.MAX_VALUE;
for (int j = i; j < n; j++) {
int curCost = (j - i + 1) * rates[i];
int nextCost = process1(powers, rates, n, j + 1, rest - powers[i], dp);
int curAns = Math.max(curCost, nextCost);
ans = Math.min(ans, curAns);
}
dp[i][rest] = ans;
return ans;
}
// 正式方法
// 时间复杂度O( N^2 * log(rates[0] * n))
// 揭示了大的思路可以继续用线段树优化枚举详情看fast3
// 解题思路:
// 二分答案
// 定义函数minPower
// 如果一定要在time时间内捡完所有衣服请返回使用最少的电量
// 如果minPower这个函数能实现
// 那么只要二分出最小的答案即可
public static int fast2(int n, int b, int[] powers, int[] rates) {
if (n == 0) {
return 0;
}
if (b == 0 || powers[0] > b) {
return -1;
}
// 最小时间只可能在[1, rates[0] * n]范围上
int l = 1;
int r = rates[0] * n;
int m = 0;
int ans = -1;
// 二分答案
// 规定的时间就是m
// minPower(powers, rates, m):
// 如果一定要在time时间内捡完所有衣服返回最小电量
// 如果这个最小电量 <= 总电量说明m时间可行左侧继续二分答案
// 如果这个最小电量 > 总电量说明m时间不可行右侧继续二分答案
while (l <= r) {
m = (l + r) / 2;
if (minPower2(powers, rates, m) <= b) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
// 给定所有机器人的启动电量 powers[]
// 给定所有机器人的收一件衣服的时间 rates[]
// 一定要在time时间内收完所有衣服
// 返回 : 至少需要的电量!
public static int minPower2(int[] powers, int[] rates, int time) {
int[] dp = new int[powers.length];
Arrays.fill(dp, -1);
return process2(powers, rates, 0, time, dp);
}
// i....这么多的衣服
// 在time时间内一定要收完
// 返回最小电量
// 如果怎么都收不完,返回系统最大值
// N^2
public static int process2(int[] powers, int[] rates, int i, int time, int[] dp) {
int n = powers.length;
if (i == n) {
return 0;
}
if (dp[i] != -1) {
return dp[i];
}
// i.....
// 收当前i位置这一件衣服的时间
int usedTime = rates[i];
int nextMinPower = Integer.MAX_VALUE;
for (int j = i; j < n && usedTime <= time; j++) {
// i...i i+1....
// i......i+1 i+2...
// i...........i+2 i+3...
// i....j j+1....
nextMinPower = Math.min(nextMinPower, process2(powers, rates, j + 1, time, dp));
usedTime += rates[i];
}
int ans = nextMinPower == Integer.MAX_VALUE ? nextMinPower : (powers[i] + nextMinPower);
dp[i] = ans;
return ans;
}
// fast2的思路 + 线段树优化枚举
// 时间复杂度O(N * logN * log(rates[0] * N))
public static int fast3(int n, int b, int[] powers, int[] rates) {
if (n == 0) {
return 0;
}
if (b == 0 || powers[0] > b) {
return -1;
}
int l = 1;
int r = rates[0] * n;
int m = 0;
int ans = -1;
while (l <= r) {
m = (l + r) / 2;
if (minPower3(powers, rates, m) <= b) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
public static int minPower3(int[] powers, int[] rates, int time) {
int n = powers.length;
int[] dp = new int[n + 1];
// dp[n-1] dp[n]
// n-1 n
SegmentTree st = new SegmentTree(n + 1);
st.update(n, 0);
for (int i = n - 1; i >= 0; i--) {
if (rates[i] > time) {
dp[i] = Integer.MAX_VALUE;
} else {
int j = Math.min(i + (time / rates[i]) - 1, n - 1);
// for.... logN
int next = st.min(i + 1, j + 1);
int ans = next == Integer.MAX_VALUE ? next : (powers[i] + next);
dp[i] = ans;
}
st.update(i, dp[i]);
}
return dp[0];
}
public static class SegmentTree {
private int n;
private int[] min;
public SegmentTree(int size) {
n = size + 1;
min = new int[n << 2];
Arrays.fill(min, Integer.MAX_VALUE);
}
private void pushUp(int rt) {
min[rt] = Math.min(min[rt << 1], min[rt << 1 | 1]);
}
public void update(int i, int v) {
update(i + 1, i + 1, v, 1, n, 1);
}
private void update(int L, int R, int C, int l, int r, int rt) {
if (L <= l && r <= R) {
min[rt] = C;
return;
}
int mid = (l + r) >> 1;
if (L <= mid) {
update(L, R, C, l, mid, rt << 1);
}
if (R > mid) {
update(L, R, C, mid + 1, r, rt << 1 | 1);
}
pushUp(rt);
}
public int min(int l, int r) {
return min(l + 1, r + 1, 1, n, 1);
}
private int min(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
return min[rt];
}
int mid = (l + r) >> 1;
int left = Integer.MAX_VALUE;
int right = Integer.MAX_VALUE;
if (L <= mid) {
left = min(L, R, l, mid, rt << 1);
}
if (R > mid) {
right = min(L, R, mid + 1, r, rt << 1 | 1);
}
return Math.min(left, right);
}
}
// 为了测试
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 = 200;
int B = 100;
int P = 20;
int R = 10;
int testTimes = 5000;
System.out.println("功能测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int b = (int) (Math.random() * B) + 1;
int[] powers = randomArray(n, P);
int[] rates = randomArray(n, R);
int ans1 = fast1(n, b, powers, rates);
int ans2 = fast2(n, b, powers, rates);
int ans3 = fast3(n, b, powers, rates);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("出错了!");
System.out.println(ans1);
System.out.println(ans2);
System.out.println(ans3);
break;
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
int n = 10000;
int b = 100000;
int[] powers = randomArray(n, b);
int[] rates = randomArray(n, b);
System.out.println("衣服规模 : " + n);
System.out.println("电量规模 : " + b);
System.out.println("机器人启动费用取值规模 : " + b);
System.out.println("机器人工作速度取值规模 : " + b);
long start = System.currentTimeMillis();
fast3(n, b, powers, rates);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}

@ -0,0 +1,196 @@
package class_2022_09_1_week;
// 来自学员问题
// 给你一个长度为n的数组并询问q次
// 每次询问区间[l,r]之间是否存在小于等于k个数的和大于等于x
// 每条查询返回true或者false
// 1 <= n, q <= 10^5
// k <= 10
// 1 <= x <= 10^8
import java.util.PriorityQueue;
public class Code04_QueryTopKSum {
public static class SegmentTree {
private int n;
private int k;
// private int[] max;
// private int[][] max = new int[][10];
private int[][] max;
private int[][] query;
public SegmentTree(int[] arr, int K) {
n = arr.length;
k = K;
max = new int[(n + 1) << 2][k];
query = new int[(n + 1) << 2][k];
for (int i = 0; i < n; i++) {
update(i, arr[i]);
}
}
public int topKSum(int l, int r) {
collect(l + 1, r + 1, 1, n, 1);
int sum = 0;
for (int num : query[1]) {
sum += num;
}
return sum;
}
private void update(int i, int v) {
update(i + 1, i + 1, v, 1, n, 1);
}
private void update(int L, int R, int C, int l, int r, int rt) {
if (L <= l && r <= R) {
max[rt][0] = C;
return;
}
int mid = (l + r) >> 1;
if (L <= mid) {
update(L, R, C, l, mid, rt << 1);
}
if (R > mid) {
update(L, R, C, mid + 1, r, rt << 1 | 1);
}
merge(max[rt], max[rt << 1], max[rt << 1 | 1]);
}
// father 要前k名
// left k名
// right k名
private void merge(int[] father, int[] left, int[] right) {
for (int i = 0, p1 = 0, p2 = 0; i < k; i++) {
if (left == null || p1 == k) {
father[i] = right[p2++];
} else if (right == null || p2 == k) {
father[i] = left[p1++];
} else {
father[i] = left[p1] >= right[p2] ? left[p1++] : right[p2++];
}
}
}
private void collect(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
for (int i = 0; i < k; i++) {
query[rt][i] = max[rt][i];
}
} else {
int mid = (l + r) >> 1;
boolean leftUpdate = false;
boolean rightUpdate = false;
if (L <= mid) {
leftUpdate = true;
collect(L, R, l, mid, rt << 1);
}
if (R > mid) {
rightUpdate = true;
collect(L, R, mid + 1, r, rt << 1 | 1);
}
merge(query[rt], leftUpdate ? query[rt << 1] : null, rightUpdate ? query[rt << 1 | 1] : null);
}
}
}
// 暴力实现的结构
// 为了验证
public static class Right {
public int[] arr;
public int k;
public Right(int[] nums, int K) {
k = K;
arr = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
arr[i] = nums[i];
}
}
public int topKSum(int l, int r) {
PriorityQueue<Integer> heap = new PriorityQueue<>((a, b) -> b - a);
for (int i = l; i <= r; i++) {
heap.add(arr[i]);
}
int sum = 0;
for (int i = 0; i < k && !heap.isEmpty(); i++) {
sum += heap.poll();
}
return sum;
}
}
// 为了验证
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 = 100;
int K = 10;
int V = 100;
int testTimes = 5000;
int queryTimes = 100;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int k = (int) (Math.random() * K) + 1;
int[] arr = randomArray(n, V);
SegmentTree st = new SegmentTree(arr, k);
Right right = new Right(arr, k);
for (int j = 0; j < queryTimes; j++) {
int a = (int) (Math.random() * n);
int b = (int) (Math.random() * n);
int l = Math.min(a, b);
int r = Math.max(a, b);
int ans1 = st.topKSum(l, r);
int ans2 = right.topKSum(l, r);
if (ans1 != ans2) {
System.out.println("出错了!");
System.out.println(ans1);
System.out.println(ans2);
}
}
}
System.out.println("测试结束");
System.out.println("性能测试开始");
int n = 100000;
int k = 10;
int[] arr = randomArray(n, n);
int[][] queries = new int[n][2];
for (int i = 0; i < n; i++) {
int a = (int) (Math.random() * n);
int b = (int) (Math.random() * n);
queries[i][0] = Math.min(a, b);
queries[i][1] = Math.max(a, b);
}
System.out.println("数据规模 : " + n);
System.out.println("数值规模 : " + n);
System.out.println("查询规模 : " + n);
System.out.println("k规模 : " + k);
long start, end1, end2;
start = System.currentTimeMillis();
SegmentTree st = new SegmentTree(arr, k);
end1 = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
st.topKSum(queries[i][0], queries[i][1]);
}
end2 = System.currentTimeMillis();
System.out.println("初始化运行时间 : " + (end1 - start) + " 毫秒");
System.out.println("执行查询运行时间 : " + (end2 - end1) + " 毫秒");
System.out.println("总共运行时间 : " + (end2 - start) + " 毫秒");
System.out.println("性能测试结束");
}
}

@ -0,0 +1,77 @@
package class_2022_09_1_week;
// 来自微软面试
// 给定一个长度为n的二维数组graph代表一张图
// graph[i] = {a,b,c,d} 表示i讨厌(a,b,c,d),讨厌关系为双向的
// 一共有n个人编号0~n-1
// 讨厌的人不能一起开会
// 返回所有人能不能分成两组开会
// 测试链接 : https://leetcode.cn/problems/is-graph-bipartite/
public class Code05_IsGraphBipartite {
public boolean isBipartite(int[][] graph) {
int n = graph.length;
UnionFind uf = new UnionFind(n);
for (int[] neighbours : graph) {
for (int i = 1; i < neighbours.length; i++) {
uf.union(neighbours[i - 1], neighbours[i]);
}
}
for (int i = 0; i < n; i++) {
for (int j : graph[i]) {
if (uf.same(i, j)) {
return false;
}
}
}
return true;
}
public static class UnionFind {
private int[] f;
private int[] s;
private int[] h;
public UnionFind(int n) {
f = new int[n];
s = new int[n];
h = new int[n];
for (int i = 0; i < n; i++) {
f[i] = i;
s[i] = 1;
}
}
private int find(int i) {
int hi = 0;
while (i != f[i]) {
h[hi++] = i;
i = f[i];
}
while (hi > 0) {
f[h[--hi]] = i;
}
return i;
}
public boolean same(int i, int j) {
return find(i) == find(j);
}
public void union(int i, int j) {
int fi = find(i);
int fj = find(j);
if (fi != fj) {
if (s[fi] >= s[fj]) {
f[fj] = fi;
s[fi] = s[fi] + s[fj];
} else {
f[fi] = fj;
s[fj] = s[fi] + s[fj];
}
}
}
}
}

@ -1777,4 +1777,82 @@ n <= 10^6
第039节 2022年9月第1周流行算法题目解析
给你一个由小写字母组成的字符串 s ,和一个整数 k
如果满足下述条件,则可以将字符串 t 视作是 理想字符串
t 是字符串 s 的一个子序列。
t 中每两个 相邻 字母在字母表中位次的绝对差值小于或等于 k 。
返回 最长 理想字符串的长度。
字符串的子序列同样是一个字符串,并且子序列还满足:
可以经由其他字符串删除某些字符(也可以不删除)但不改变剩余字符的顺序得到。
注意:字母表顺序不会循环
例如,'a' 和 'z' 在字母表中位次的绝对差值是 25而不是 1 。
测试链接 : https://leetcode.cn/problems/longest-ideal-subsequence/
来自美团
有n个城市城市从0到n-1进行编号。小美最初住在k号城市中
在接下来的m天里小美每天会收到一个任务
她可以选择完成当天的任务或者放弃该任务
第i天的任务需要在ci号城市完成如果她选择完成这个任务
若任务开始前她恰好在ci号城市则会获得ai的收益
若她不在ci号城市她会前往ci号城市获得bi的收益
当天的任务她都会当天完成
任务完成后她会留在该任务所在的ci号城市直到接受下一个任务
如果她选择放弃任务,她会停留原地,且不会获得收益
小美想知道,如果她合理地完成任务,最大能获得多少收益
输入描述: 第一行三个正整数n, m和k表示城市数量总天数初始所在城市
第二行为m个整数c1, c2,...... cm其中ci表示第i天的任务所在地点为ci
第三行为m个整数a1, a2,...... am其中ai表示完成第i天任务且地点不变的收益
第四行为m个整数b1, b2,...... bm其中bi表示完成第i天的任务且地点改变的收益
0 <= k, ci <= n <= 30000
1 <= m <= 30000
0 <= ai, bi <= 10^9
输出描述 输出一个整数,表示小美合理完成任务能得到的最大收益
来自美团
给定一个正数n, 表示从0位置到n-1位置每个位置放着1件衣服
从0位置到n-1位置不仅有衣服每个位置还摆着1个机器人
给定两个长度为n的数组powers和rates
powers[i]表示i位置的机器人的启动电量
rates[i]表示i位置的机器人收起1件衣服的时间
使用每个机器人只需要付出启动电量
当i位置的机器人收起i位置的衣服它会继续尝试往右收起i+1位置衣服
如果i+1位置的衣服已经被其他机器人收了或者其他机器人正在收
这个机器人就会停机, 不再收衣服。
不过如果它不停机它会同样以rates[i]的时间来收起这件i+1位置的衣服
也就是收衣服的时间为每个机器人的固定属性当它收起i+1位置的衣服
它会继续检查i+2位置...一直到它停机或者右边没有衣服可以收了
形象的来说机器人会一直尝试往右边收衣服收k件的话就耗费k * rates[i]的时间
但是当它遇见其他机器人工作的痕迹,就会认为后面的事情它不用管了,进入停机状态
你手里总共有电量b准备在0时刻将所有想启动的机器人全部一起启动
过后不再启动新的机器人并且启动机器人的电量之和不能大于b
返回在最佳选择下,假快多久能收完所有衣服
如果无论如何都收不完所有衣服,返回-1
给定数据: int n, int b, int[] powers, int[] rates
数据范围:
powers长度 == rates长度 == n <= 1000
1 <= b <= 10^5
1 <= powers[i]、rates[i] <= 10^5
来自学员问题
给你一个长度为n的数组并询问q次
每次询问区间[l,r]之间是否存在小于等于k个数的和大于等于x
每条查询返回true或者false
1 <= n, q <= 10^5
k <= 10
1 <= x <= 10^8
来自微软面试
给定一个长度为n的二维数组graph代表一张图
graph[i] = {a,b,c,d} 表示i讨厌(a,b,c,d),讨厌关系为双向的
一共有n个人编号0~n-1
讨厌的人不能一起开会
返回所有人能不能分成两组开会
测试链接 : https://leetcode.cn/problems/is-graph-bipartite/

Loading…
Cancel
Save