思路借鉴于2024.3.24小红书暑期实习笔试多语言AK指南 - 知乎 (zhihu.com),主要是针对真题的二次解释,所有程序仅通过样例,可能存在错误,最后一题暂时没用java重写。
难度:第一、二道是简单题,第三道是略难的简单题,第四道和第五道是中等偏上题。
第一道是“模拟 + 哈希表”,第二道是“三维数组动态规划”(第三维<=2),第三道是“模拟+二分查找”
T1
样例
输入
8
a
b
a
c
a
a
a
a
输出
a
b
c
思路:模拟 哈希表
我们可以维护一个哈希表来记录当前单词有没有出现,如果没出现过,则说明是第一次出现,直接输出当前单词,并将当前单词添加到哈希表中即可 时间复杂度:O(n)
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Set<String> st = new HashSet<>(); // 判断当前单词是否出现过
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
for(int i = 0; i < n; i++) {
String s = scanner.next();
if(!st.contains(s)) { // 是否包含
System.out.println(s); // 如果不包含,则输出
}
st.add(s); // 加入Set中
}
}
}
T2
样例
输入
5 8
1 2 3 4 10
输出
2
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建一个 Scanner 对象,用于读取输入
int n = scanner.nextInt(); // 读取旧帐号的数量
int x = scanner.nextInt(); // 读取新账号需要的粉丝数
int[] w = new int[n + 1]; // 创建数组用于存储每个旧帐号的粉丝数量
for(int i = 1; i <= n; i++) w[i] = scanner.nextInt(); // 读取每个旧帐号的粉丝数量并存储到数组中
// 定义为从前i个旧账号中选择,且粉丝量为j,使用了k次多次推广(k<=1)的最小选择的旧帐号数量
int[][][] f = new int[n + 1][x + 1][2]; // 用于动态规划
for(int[][] arr : f) { // 初始化数组,将所有元素设置为一个较大的值
for(int[] subArr : arr) {
Arrays.fill(subArr, Integer.MAX_VALUE / 2);
}
}
f[0][0][0] = 0; // 初始状态
for(int i = 1; i <= n; i++) { // 动态规划主循环,遍历旧帐号
for(int j = 0; j <= x; j++) { // 遍历粉丝数
for(int k = 0; k < 2; k++) { // 遍历状态
// 不变或不选择当前旧帐号,一开始 Integer.MAX_VALUE / 2 > f[i-1][j][k],之后就是正常比较
f[i][j][k] = Math.min(f[i][j][k], f[i - 1][j][k]);
if(j >= w[i] / 2) { // 如果当前还需要的粉丝数>=选取当前帐号一半的粉丝数,例如 6 > 4 > 3(6/2)
// 不变或者选取该账号的一半粉丝数,选取最小值
f[i][j][k] = Math.min(f[i][j][k], f[i - 1][j - w[i] / 2][k] + 1);
}
if(j >= w[i] && k > 0) { // 如果当前还需要的粉丝数>=选取当前帐号,并且判断是否进行一次多次推荐(仅当目前粉丝数>=该账户拥有的粉丝数才进行)
// 不变或者选取该账号的所有的粉丝数,选取最小值
// 注意此处需要k-1,因为是多次推荐(要第i个账号的全部粉丝)
f[i][j][k] = Math.min(f[i][j][k], f[i - 1][j - w[i]][k - 1] + 1);
}
}
}
}
int res = Math.min(f[n][x][0], f[n][x][1]); // 判断是否需要多次推荐
if(res == Integer.MAX_VALUE / 2) System.out.println("-1"); // 找不到合适的数组成推荐数
else System.out.println(res);
}
}
T3
样例
输入
3
3 1 4
输出
9
15
8
import java.util.*;
public class Main {
static int n; // 题解数量
static int[] a; // 每个笔记的点赞数
static long sum = 0; // 原始的总点赞数
// c1是当前笔记点赞数的增量,即最大点赞数x=c1+当前点赞数
// c2是除当前笔记点赞数的增量,即c2 = x * (n - 1) - (sum - a_i)
// 因为是当前笔记和其他笔记交替点赞,所以c1-c2<=1,这里先点击当前笔记,即先点击c1
// 检查当前点赞数是否符合条件
static boolean check(long a_i, long x) {
// 如果当前点赞数 x 减去当前笔记的点赞数 a_i 小于等于剩余笔记的总点赞数加上 1,那么就认为当前点赞数 x 可以满足条件,即当前笔记所需的额外点赞数小于等于剩余笔记的总点赞数加上 1。
// 也就是说只允许 x - a_i
return x - a_i <= x * (n - 1) - (sum - a_i) + 1; // c1 <= c2+1
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt(); // 读取题解数量n
a = new int[n]; // 初始化数组用于存储每个笔记的点赞数
int maxv = 0; // 初始化最大点赞数
// 读取每个笔记的点赞数,并计算总点赞数和最大点赞数
for (int i = 0; i < n; i++) {
a[i] = scanner.nextInt();
sum += a[i];
maxv = Math.max(maxv, a[i]); // 更新最大点赞数
}
// 处理特殊情况:如果题解数量为2
if (n == 2) {
for (int i = 0; i < n; i++) {
if (a[i] == maxv) System.out.println(a[i]); // 如果当前笔记的点赞数是最大点赞数,输出该点赞数
else System.out.println("-1"); // 否则输出-1
}
}
// 对于每个笔记,计算满足条件的最小点赞数,在[maxv,1e12]中寻找
for (int i = 0; i < n; i++) {
long l = maxv, r = (long)1e12; // 设置二分搜索的左右边界
while (l < r) {
long mid = l + (r - l) / 2; // 计算中间点
if (check(a[i], mid)) r = mid; // 如果当前点赞数符合条件,将右边界缩小到mid
else l = mid + 1; // 否则将左边界扩大到mid+1
}
// 计算并输出最小点赞数,总点赞数加上其他笔记的最大点赞数减去当前笔记的点赞数(除当前笔记外其他笔记的总点赞数),并加上0或当前笔记的最大点赞数减去当前笔记的点赞数再减去1中的最大值(除当前笔记外其他笔记的最大点赞数。)。
// res = sum + c1 + c2(c2>=0),原始总点赞数+当前笔记点赞增量+其他笔记点赞增量
long res = sum + l - a[i] + Math.max(0, l - a[i] - 1);
System.out.println(res);
}
}
}