小红书笔试 20250824
笔试时间:2025年8月24日
往年笔试合集:
第一题:行为权重
小红是小红书的用户行为分析师。平台将每次用户行为映射为一个正整数权重序列 [a₁, a₂, …, aₙ],以便后续关联推荐时关键 “红色” 行为。
为了保证标记的行为具有足够的共性,必须选出的所有 “红色” 行为权重的最大公约数大于 1;同时,为了避免相邻生冗余,所选下标不得相邻。
现给定用户的一次行为序列,求最多可以染成红色的行为数量。
【名词解释】
最大公约数:指一组整数共有约数中最大的一个。例如,12、18 和 30 的公约数有 1,2,3,6,其中最大的约数是 6,因此 gcd (12,18,30)=6。
输入描述
在一行上输入一个整数 n(1 ≤ n ≤ 10⁵),表示行为序列长度。
在第二行输入 n 个整数 a₁, a₂, …, aₙ(1 ≤ aᵢ ≤ 100),表示每次行为的权重值。
输出描述
在一行上输出一个整数,表示最多可染红的行为数量。
样例输入
5 1 2 3 2 6
样例输出
2
在这个样例中,可将下标 2 与 4 对应的权重染红,它们的最大公约数为 2,且不相邻,故答案为 2。
参考题解
直接找出满足所有条件的最佳组合很复杂。我们可以反过来想:既然所有选中的数必须有一个大于1的最大公约数(GCD),那么它们必然都是某个整数 d (d>1) 的倍数。由于题目给的数字最大不超过100,这个公约数 d 也肯定在2到100之间。因此,我们可以把复杂问题分解成近百个简单问题:第一步:锁定公约数 d我们假设最终选出的所有“红色”行为,它们的公约数就是 d。我们依次让 d 等于 2、3、4、...、100,分别计算每种情况下的最优解。第二步:解决简化后的问题 当公约数 d 固定后,问题就变成了:在原数组中,最多能选出多少个“不相邻”的、且是 d 的倍数的数?第三步:动态规划(“打家劫舍”模型)这个简化后的问题是一个非常经典的动态规划模型,可以类比为“打家劫舍”(不能偷相邻的屋子)。 我们从头到尾遍历数组,对每个位置 i 的数 a[i],我们做决策:不选 a[i]:最大数量等于到位置 i-1 的最大数量。选 a[i]:因为不能和相邻的 a[i-1] 一起选,所以最大数量是 1 (代表 a[i] 自己) + 到位置 i-2 的最大数量。我们在这两个选择中取一个更大的结果。如果 a[i] 不是 d 的倍数:我们肯定不能选它,所以到位置 i 为止的最大数量,就等于到位置 i-1 的最大数量。如果 a[i] 是 d 的倍数:我们有两个选择:第四步:取全局最优解我们对每一个 d(从2到100)都执行一次第三步的动态规划,会得到近百个结果。在所有这些结果中,取那个最大的数,就是题目的最终答案。
C++:
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { int n; cin >> n; vector<int> a(n); for (int i = 0; i < n; ++i) { cin >> a[i]; } int ans = 0; for (int d = 2; d <= 100; ++d) { int prev1 = 0, prev2 = 0; for (int i = 0; i < n; ++i) { int temp1 = prev2; int temp2 = 0; if (a[i] % d == 0) { temp2 = 1 + prev1; } int current = max(temp1, temp2); prev1 = prev2; prev2 = current; } if (prev2 > ans) { ans = prev2; } } cout << ans << endl; return 0; }
Java:
import java.util.Scanner; public class MaxDivisibleSubsequence { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int[] a = new int[n]; for (int i = 0; i < n; ++i) { a[i] = scanner.nextInt(); } int ans = 0; for (int d = 2; d <= 100; ++d) { int prev1 = 0, prev2 = 0; for (int i = 0; i < n; ++i) { int temp1 = prev2; int temp2 = 0; if (a[i] % d == 0) { temp2 = 1 + prev1; } int current = Math.max(temp1, temp2); prev1 = prev2; prev2 = current; } if (prev2 > ans) { ans = prev2; } } System.out.println(ans); scanner.close(); } }
Python:
import sys def main(): n = int(sys.stdin.readline()) a = list(map(int, sys.stdin.readline().split())) ans = 0 for d in range(2, 101): prev1, prev2 = 0, 0 for i in range(n): temp1 = prev2 temp2 = 0 if a[i] % d == 0: temp2 = 1 + prev1 current = max(temp1, temp2) prev1, prev2 = prev2, current if prev2 > ans: ans = prev2 print(ans) if __name__ == "__main__": main()
第二题:潜在同好
在小红书平台的社交推荐项目中,产品团队希望基于用户的日常行为习惯分数,挖掘潜在的 “同好” 关系。系统简化如下,数据库中有 n 个用户的日常行为习惯分数,第 i 个用户的分数使用 aᵢ表示。记第 i 个用户和第 j 个用户构成 “同好” 关系,当且仅当 aᵢ能被 aⱼ整除,或者 aⱼ能被 aᵢ整除。接下来将进行 m 次查询,每次给定一个额外的用户行为分数 x,请统计在数据库中,有多少不同的人能与这个人构成 “同好” 关系。
输入描述
第一行输入两个整数 n,m(1 ≤ n,m ≤ 5×10⁵),表示数据库中用户数量、查询次数。
第二行输入 n 个整数 a₁,a₂,…,aₙ(1 ≤ aᵢ ≤ 5×10⁵),表示数据库中的用户日常行为习惯分数。
接下来 m 行,每行输入一个整数 x(1 ≤ x ≤ 5×10⁵),表示一个额外的用户行为习惯分数。
输出描述
对于每次查询,新起一行,输出一个整数,表示数据库中能与 x 构成 “同好” 关系的用户数量。
样例输入
5 3
1 2 2 5 6
4
2
1
样例输出
3
4
5
参考题解
预处理 + 容斥原理。核心公式对于每次查询 x,与其构成“同好”关系的人数等于: (数据库中 x 的倍数个数) + (数据库中 x 的因数个数) - (数据库中 x 自身的个数) 减去 x 自身的个数是因为它在“倍数”和“因数”中被计算了两次。优化方法直接对每次查询计算上述数量会超时。由于查询次数多而数值范围固定,我们采用“空间换时间”的策略,进行预处理。预处理步骤我们使用三个数组,提前算好所有可能用到的信息:这两个关键数组 (multiples_count 和 divisors_sum) 都可以通过类似筛法的循环在 O(MAXlogMAX) 的高效时间内计算完成。counts[k]: 统计分数 k 的出现次数。multiples_count[k]: 预计算数据库中 k 的倍数总共有多少个。divisors_sum[k]: 预计算数据库中 k 的因数总共有多少个。快速查询 预处理完成后,对于任何查询 x,我们只需从预处理好的数组中直接取值,代入第一步的核心公式,即可在 O(1) 时间内得出答案。
C++:
#include <iostream> #include <vector> #include <string> using namespace std; const int M = 500001; vector<int> f1(int n, vector<int>& arr, int sz) { vector<int> cnt(sz + 1, 0); for (int v : arr) { if (v <= sz) { cnt[v]++; } } return cnt; } vector<int> f2(vector<int>& cnt, int sz) { vector<int> mc(sz + 1, 0); for (int i = 1; i <= sz; ++i) { int t = i; while (t <= sz) { mc[i] += cnt[t]; t += i; } } return mc; } vector<int> f3(vector<int>& cnt, int sz) { vector<int> ds(sz + 1, 0); for (int i = 1; i <= sz; ++i) { if (cnt[i] > 0) { int j = i; while (j <= sz) { ds[j] += cnt[i]; j += i; } } } return ds; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n_val, m_val; cin >> n_val >> m_val; vector<int> nums(n_val); for (int i = 0; i < n_val; ++i) { cin >> nums[i]; } vector<int> cnt_arr = f1(n_val, nums, M); vector<int> mc_arr = f2(cnt_arr, M); vector<int> ds_arr = f3(cnt_arr, M); vector<string> output; for (int i = 0; i < m_val; ++i) { int q; cin >> q; if (q >= M) { output.push_back("0\n"); } else { int result = mc_arr[q] + ds_arr[q] - cnt_arr[q]; output.push_back(to_string(result) + "\n"); } } for (const string& line : output) { cout << line; } return 0; }
Java:
import java.io.*; import java.util.*; public class ArrayAnalyzer { private static final int M = 500001; public static int[] f1(int n, int[] arr, int sz) { int[] cnt = new int[sz + 1]; for (int v : arr) { if (v <= sz) { cnt[v]++; } } return cnt; } public static int[] f2(int[] cnt, int sz) { int[] mc = new int[sz + 1]; for (int i = 1; i <= sz; i++) { int t = i; while (t <= sz) { mc[i] += cnt[t]; t += i; } } return mc; } public static int[] f3(i
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南