力扣第460场周赛
补题
https://leetcode.cn/contest/weekly-contest-460/
第一题
链接
题解
翻译一下题意:给一个 nums
数组,可以正好分成若干个元素个数为 3
的子数组,将子数组升序排序,求每个子数组中间元素相加之和的最大值。
看一下数据范围,nums.length
在 10^6
左右,所以大概要用 O(nlogn)
内的算法,可以先排除暴力枚举了。
我是从子数组的角度下手的,按照升序排序再选中间元素,说明对于一个组合,我们总是只能选择第二大的元素,而第一大的元素总是无法选择的,因为组合一个子数组后,需要删除初始数组 3
个元素。
可以得到这样一个贪心策略:
对初始数组升序排序,从大到小,两个元素一个组合,组合中较大元素不能选,较小元素为能选择的最大元素。依次相加 nums.length / 3
个较小元素,得到中位数之和的最大值。
代码
1
2
3
4
5
6
7
8
9
public long maximumMedianSum(int[] nums) {
Arrays.sort(nums);
int n = nums.length / 3;
long res = 0;
for (int i = 1; i <= n; i++) {
res += nums[nums.length - i * 2];
}
return res;
}
第二题
链接
题解
枚举插入,再统计子序列个数,求最大值。
有两个需要优化掉暴力的点:
第一点:'L'
插入开头总是最优解,'T'
插入结尾总是最优解。
'C'
插入一个位置 i
所增加的子序列数为:i
前 L
个数 乘 i
后 T
个数,所以可以预处理求出最佳索引。
第二点:对 LCT
子序列的计数要将 O(n^3)
优化到 O(n)
,用前缀计数的技巧,见下方 clct
函数。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public long numOfSubsequences(String s) {
// 长度为1的直接返回
if (s.length() < 2)
return 0;
int n = s.length();
int[] l = new int[n + 1], t = new int[n + 1];
for (int i = 1; i <= n; i++) {
l[i] = l[i - 1];
if (s.charAt(i - 1) == 'L')
l[i]++;
}
for (int i = n - 1; i >= 0; i--) {
t[i] = t[i + 1];
if (s.charAt(i) == 'T')
t[i]++;
}
long max = 0;
int idx = -1;
for (int i = 0; i <= n; i++) {
long tmp = (long) l[i] * t[i];
if (tmp > max) {
max = tmp;
idx = i;
}
}
long res = clct('L' + s);
// 说明 L 和 T 中少一个,不可能插入 C
if (idx != -1) {
res = Math.max(res, clct(s.substring(0, idx) + 'C' + s.substring(idx)));
}
res = Math.max(res, clct(s + 'T'));
return res;
}
// 子序列统计常用技巧
private long clct(String s) {
long l = 0, lc = 0, lct = 0;
for (char c : s.toCharArray()) {
if (c == 'L') {
l++;
} else if (c == 'C') {
lc += l;
} else if (c == 'T') {
lct += lc;
}
}
return lct;
}
第三题
链接
题解
总结题意:给一个长度为 n
的整数数组 nums
,下标 0
为起点,下标 n - 1
为终点。
假设此时在下标 i
处,移动方式有两个:
- 左右移动:移动到
i - 1
或i + 1
处; - 质数传送:
nums[i]
为质数,可传送到以nums[i]
为质因数的元素的位置。
求最小移动次数。
前半部分埃式筛法和分解质因数可以看 质数筛 和 试除法分解质因数,接下来可以看作有向无权图的问题。
数组元素作为节点,节点的出度有三种:分别是左移动,右移动,质数传送。
质数判断和找边都需要通过查表优化时间才能过。
然后在图上 BFS
找最短路径,需要注意标记访问,避免死循环。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
private static final int MAX = 1_000_001;
private static final boolean[] isPrime = new boolean[MAX];
// 预处理: 埃式筛法
static {
Arrays.fill(isPrime, true);
isPrime[0] = isPrime[1] = false;
for (int i = 2; i * i < MAX; i++) {
if (isPrime[i]) {
for (int j = i * i; j < MAX; j += i) {
isPrime[j] = false;
}
}
}
}
// 试除法分解质因数(无重复)
public List<Integer> getPrimeList(int num) {
List<Integer> factors = new ArrayList<>();
for (int prime = 2; prime < isPrime.length; prime++) {
if (!isPrime[prime]) continue;
if (prime * prime > num) break;
if (num % prime == 0) {
factors.add(prime);
while (num % prime == 0) num /= prime;
}
}
if (num > 1) factors.add(num);
return factors;
}
// 提交方法
public int minJumps(int[] nums) {
int n = nums.length;
if (n < 2) return 0;
// 预处理表,用于质数跳跃时 O(1) 找到路径
Map<Integer, List<Integer>> pLs = new HashMap<>();
for (int i = 0; i < n; i++) {
int num = nums[i];
if (num < 2) continue;
List<Integer> fs = getPrimeList(num);
for (Integer f : fs) {
pLs.computeIfAbsent(f, k -> new ArrayList<>()).add(i);
}
pLs.computeIfAbsent(num, k -> new ArrayList<>()).add(i);
}
// 先访问到的一定是最短路径,避免成环无限循环
boolean[] v = new boolean[n];
Queue<int[]> q = new LinkedList<>();
// 设计节点携带信息: 原数组索引和当前步数
q.offer(new int[]{0, 0});
v[0] = true;
// BFS 框架代码
while (!q.isEmpty()) {
int[] curr = q.poll();
int index = curr[0], steps = curr[1];
// 向前一步
if (index + 1 < n && !v[index + 1]) {
if (index + 1 == n - 1) return steps + 1;
v[index + 1] = true;
q.offer(new int[]{index + 1, steps + 1});
}
// 向后一步
if (index - 1 >= 0 && !v[index - 1]) {
v[index - 1] = true;
q.offer(new int[]{index - 1, steps + 1});
}
// 质数传送
int p = nums[index];
if (p > 1 && p <= MAX && isPrime[p]) {
List<Integer> pL = pLs.getOrDefault(p, new ArrayList<>());
for (int i : pL) {
if (!v[i] && i != index) {
if (i == n - 1) return steps + 1;
v[i] = true;
q.offer(new int[]{i, steps + 1});
}
}
}
}
return -1;
}
代码参考: 力扣题解
第四题
链接
题解
看不懂,没招了
代码
前面的区域,以后再来探索吧。