字节秋招笔试 20230903
前言:菜鸟,浅浅记录总结一下,欢迎大佬们批判。
Q1 生成字符串(模拟)
题目描述:给定整数 ,生成字符串,该字符串由二十六个小写字母组成,每个字母至少出现两次,且相同字母之间的距离为 ().
public static String solve(int k) {
StringBuilder sb = new StringBuilder();
Deque<Character> queue = new ArrayDeque<>();
int[] cnt = new int[26];
// 依次放入26个字母
for (int i = 0; i < 26;) {
// 倒数第k个字母数量少于2时优先加入
if (queue.size() >= k && cnt[queue.peekFirst() - 'a'] < 2) {
sb.append(queue.peekFirst());
queue.offerLast(queue.peekFirst());
cnt[queue.peekFirst() - 'a']++;
} else { // 倒数第k个字母数量满足后,加入下一个字母
char c = (char)('a' + i);
sb.append(c);
queue.offerLast(c);
cnt[i]++;
i++;
}
// 字符队列始终记录后k个字符
if (queue.size() > k) {
queue.pollFirst();
}
}
// 当最后一个字符数量未达到2时一直循环加入倒数第k个字符
while (cnt[25] < 2) {
sb.append(queue.peekFirst());
queue.offerLast(queue.peekFirst());
cnt[queue.peekFirst() - 'a']++;
queue.pollFirst();
}
return sb.toString();
}
Q2 求连通块的权重(找规律)
题目描述:给定一个无限大小的由 组成矩阵,矩阵中每两个相邻元素均不相同(即0,1间隔排列)。给定连通块的权重定义为:元素 的数量减去元素 的数量。给定连通块元素数量 ,求出最大权重。
public static long solve(long k) {
if (k == 0 || k == 1) {
return k;
}
// 从第二个元素起找规律
k--;
// 连通块最大权重的变化规律为 -1, +1, +1, +1 ... -1, +1, +1, +1.
long res = k / 4 * 2;
k -= k / 4 * 4;
// k 的取值为 0 / 1 / 2 / 3.
if (k == 1) {
res -= 1;
}
if (k == 2 || k == 3) {
res += k - 2;
}
// 加上第一个元素 1.
res++;
return res;
}
Q3 红色连通块数量(并查集)
题目描述:给定 个节点和它们对应的颜色字符串 ,对应的字符为 代表白色,为 代表红色。 为节点间的边。当一个连通块的所有节点均为红色时,称为一个红色连通块。请给出将第 个节点染红后,红色连通块的数量。
(相同思路笔试时超时了(没有去除一些情况),目前版本不知是否能通过,暂且记录QAQ)
public static void solve(String color, int[][] edges) {
// 并查集维护连通量和联通关系
Map<Integer, Integer> root = new HashMap<>();
int count = 0;
int n = color.length();
List<Integer>[] adjlist = new List[n];
for (int i = 0; i < n; i++) {
adjlist[i] = new ArrayList<>();
root.put(i, i);
// 初始连通分量大小为红色节点数量
if (color.charAt(i) == 'r') {
count++;
}
}
// 对于红色节点间的边直接通过并查集维护,红白之间则放入adjlist中待之后染色使用
for (int[] edge : edges) {
int u = edge[0], v = edge[1];
if (color.charAt(u) == 'r' && color.charAt(v) == 'r') {
count = union(u, v, root, count);
}
if (color.charAt(u) != 'r' && color.charAt(v) == 'r') {
adjlist[u].add(v);
}
if (color.charAt(u) == 'r' && color.charAt(v) != 'r') {
adjlist[v].add(u);
}
}
// 结果列表
int[] res = new int[n];
for (int i = 0; i < n; i++) {
// 原本就是红色节点,结果即为当前连通分量
if (color.charAt(i) == 'r') {
res[i] = count;
} else {
// 原本为白色节点,染色并合并相应的边
Map<Integer, Integer> tempRoot = new HashMap<>(root);
int tempCount = count + 1;
for (int v : adjlist[i]) {
tempCount = union(i, v, tempRoot, tempCount);
}
res[i] = tempCount;
}
}
System.out.print(Arrays.toString(res));
}
// 并查集——查找操作
public static int find(int u, Map<Integer, Integer> root) {
if (u == root.get(u)) {
return u;
}
return find(root.get(u), root);
}
// 并查集——合并操作
public static int union(int u, int v, Map<Integer, Integer> root, int count) {
int rootU = find(u, root);
int rootV = find(v, root);
if (rootU != rootV) {
root.put(rootU, rootV);
count--;
}
return count;
}
Q4 养蛊!(背包问题)
题目描述:给定一个整数数组 代表怪物血量,每次可选出两只怪物进行决斗,血量高的怪物存活,并且扣除另一只怪物相应的血量。要求输出最后一只怪物的血量,要求血量越少越好,并且输出决斗的序列。
参考:最后一块石头的重量 II
public static int solve(int[] hps) {
int sum = 0;
for (int hp : hps) {
sum += hp;
}
int n = hps.length, m = sum / 2;
// dp 用于记录前 i 个怪物里,j 为最多血量,最多的血量和
int[][] dp = new int[n + 1][m + 1];
// choose 用于记录是否选取该怪物
boolean[][] choose = new boolean[n + 1][m + 1];
for (int i = 1; i < n + 1; i++) {
int hp = hps[i - 1];
for (int j = m; j >= 0; j--) {
if (j >= hp) {
if (dp[i - 1][j] > dp[i - 1][j - hp] + hp) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j - hp] + hp;
choose[i][j] = true;
}
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
// 蛊王的血量 = 怪物总血量 - 一半血量下的最高血量 * 2
int res = sum - 2 * dp[n][m];
// 用两个列表维护怪物阵营
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
for (int i = n, j = m; i > 0; i--) {
if (choose[i][j]) {
list1.add(i);
j -= hps[i - 1];
} else {
list2.add(i);
}
}
// 输出怪物决斗的顺序
int i = 0, j = 0;
int hp1 = 0;
int hp2 = 0;
while (i < list1.size() && j < list2.size()) {
if (hp1 == 0) {
hp1 = hps[list1.get(i) - 1];
}
if (hp2 == 0) {
hp2 = hps[list2.get(j) - 1];
}
System.out.println("Monster " + list1.get(i) + " VS "
+ "Monster " + list2.get(j));
if (hp1 == hp2) {
hp1 = 0;
hp2 = 0;
i++;
j++;
} else if (hp1 > hp2) {
hp1 -= hp2;
hp2 = 0;
j++;
} else {
hp2 -= hp1;
hp1 = 0;
i++;
}
}
// 输出最后的怪物和剩余血量
if (hp2 == res || hp1 == res) {
System.out.println("King of Monster: " + list2.get(j));
System.out.println("HP: " + res);
}
return res;
}