0%

第307场周赛-1232名-3题

1661066854478

本期总结:#

  1. N种东西组成最大字典序回文时,先把成双的都处理掉,每组留下1个或者0个, 最后再考虑怎么拼接这些
  2. 子序列和第K个,可以考虑从最小数字开始, 每次保留当前数字,选下一个数字, 或者废弃当前数字,选下一个数字, 并都放入堆中, 这样每次可更新得到最小的子序列。

6152. 赢得比赛需要的最少训练时长 - 力扣(LeetCode)

1661066882187

最开始有点事情,解决完之后发现脑子很乱,这题目其实很简答

对于精力,可以提前算出需要的精力, 而经验可以在每次对战时判断是否要再增加。

但这题就是条件容易看漏,导致错了2次:

需要在经验和精力上都 严格超过对手才能击败他们

严格超过意味着不能等于,导致很多地方得+1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {

public int minNumberOfHours(int initialEnergy, int initialExperience, int[] energy, int[] experience) {
int exp = initialExperience;
int sum = Arrays.stream(energy).sum();
int res = Math.max(0, sum + 1 - initialEnergy);

for (int i = 0; i < experience.length;i++) {
if (exp < experience[i] + 1) {
res += experience[i] - exp + 1;
exp = experience[i]+1;
}
exp += experience[i];
}
return res;
}
}

6166. 最大回文数字 - 力扣(LeetCode)

1661067068832

这题也搞晕了,实际上很简单。

一堆已知数量的字母要组成回文数, 记住你先把所有成双成对的单独都按拿出来, 剩下的都是1个或者0个字母

然后成双的按大小顺序从外向内组成回文 , 最后最里面那个单字母的用最大的。

我最开始直接把奇数个数的字母当整体去思考了,其实应该先剔除奇数个数字母中成双的部分,只留下单个的, 最后再去考虑他

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
class Solution {
public String largestPalindromic(String num) {
int[] charCounts = new int[10];
for (char c : num.toCharArray()) {
charCounts[c-'0']++;
}
int numCount = 0;
// 最小的字母且是奇数个数的选择放最后
int endUseChar = -1;

boolean[] oneChar = new boolean[10];
for (int i = 9; i >=0;i--) {
oneChar[i] = (charCounts[i] %2 == 1);
charCounts[i] /= 2;
}

StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
for (int i = 9;i>=0;i--) {
if (i == endUseChar) {
continue;
}
char c = (char)('0' + i);
int count = charCounts[i];
if (i == 0 && sb1.length() == 0) {
continue;
}
while (count-->0) {
sb1.append(c);
sb2.append(c);
}
}

for (int i = 9;i>=0;i--) {
if (oneChar[i]) {
sb1.append((char) ('0' + i));
break;
}
}

if (sb1.length() == 0) {
return "0";
}

return sb1.append(sb2.reverse()).toString();
}
}

6154. 感染二叉树需要的总时间 - 力扣(LeetCode)

1661067234487

很容易想到bfs, 先dfs一遍,把每个点的父节点也得到。

然后做bfs即可

另外二叉树的题目中如果涉及map或者set,最好还是直接用引用做key,不容易写错

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
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
Map<TreeNode,TreeNode> parents = new HashMap<>();
TreeNode startNode;
public int amountOfTime(TreeNode root, int start) {
Queue<TreeNode> queue = new LinkedList<>();
find(root, null, start);
queue.offer(startNode);
Set<TreeNode> vis = new HashSet<>();
vis.add(startNode);

int res = 0;
while (!queue.isEmpty()) {
// 全部出队
res++;
List<TreeNode> nowNodes = new ArrayList<>(queue);
queue.clear();
for (TreeNode node : nowNodes) {
if (node.left != null && !vis.contains(node.left)) {
queue.offer(node.left);
vis.add(node.left);
}
if (node.right != null && !vis.contains(node.right)) {
queue.offer(node.right);
vis.add(node.right);
}
if (parents.get(node) != null && !vis.contains(parents.get(node))) {
queue.offer(parents.get(node));
vis.add(parents.get(node));

}
}
}
return res-1;
}

void find(TreeNode node, TreeNode parent, int start) {
if (node == null) {
return;
}
parents.put(node, parent);
if (node.val == start) {
startNode = node;
}

find(node.left, node, start);
find(node.right, node, start);
}
}

6155. 找出数组的第 K 大和 - 力扣(LeetCode)

这题好难,其实知道套路就可以了

我是已经想到先得到 正数的和sum, 然后以这个sum不断减去其他的值,这个减去的就是子序列而且要尽可能小, 同时负数要转整数, 到底是加还是减不需要我们关心。

但是怎么得到第K小的子序列和呢?

答案是用最小堆

先按从小到大排序

然后第0个数字肯定是最小的子序列

接着有2个选择:

① 保留第0个数字, 并继续选取第1个数字

② 废弃第0个数字, 并继续选取第1个数字

这样又得到2个子序列, 最小序列肯定在者2个里面

你会奇怪,者肯定是第②个最小啊?

但是继续走下去

当对于“废弃第0个数字, 并继续选取第1个数字”这个位置,你想废弃第1个数字,选第2个数字时,前面的“保留第0个数字, 并继续选取第1个数字”是可能比它小的, 所以要用堆进行保存,且这样的操作处理时不用考虑任何回溯或者重复的情况!

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
class Solution {
public long kSum(int[] nums, int k) {
long sum = Arrays.stream(nums).filter(num -> num > 0).mapToLong(num -> Long.valueOf(num)).sum();
for (int i = 0 ; i < nums.length;i++) {
nums[i] = Math.abs(nums[i]);
}
Arrays.sort(nums);
// [0]是选择剔除的子序列的和,[1]是当前这个子序列的最右边索引
// 每次选最小的出来
Queue<long[]> queue = new PriorityQueue<>((a,b) -> (a[0] > b[0] ? 1 : -1));
queue.offer(new long[]{0, 0L});
while (!queue.isEmpty()) {
long subSum = queue.peek()[0];
int index = (int)queue.poll()[1];
if (k == 1) {
return (sum - subSum);
}
k--;
if (index >= nums.length) {
continue;
}
queue.offer(new long[]{subSum + nums[index], index+1});

if (index -1 >= 0) {
queue.offer(new long[]{subSum - nums[index - 1] + nums[index], index + 1});
}
}
return -1;
}
}