小红书笔试 4.9 算法岗(3/3 附题面)
题面在代码中
A. 平衡
和昨晚的美团笔试差不多,先一遍dfs处理以sz[i], 得到以 i 为根的子树大小,枚举边求答案即可。
/* 小红书 23届补录&24届实习 【24届实习】算法笔试 */ #include<bits/stdc++.h> #define debug(x) std::cerr << x << '\n'; #define all(x) x.begin(), x.end() using i64 = long long; using pii = std::pair<int, int>; using vi = std::vector<int>; using vp = std::vector<pii>; using vl = std::vector<i64>; struct Edge { int u, v; } edge[1000005]; vi G[1000005]; int sz[1000005]; void dfs(int u, int par) { sz[u] = 1; for (auto v : G[u]) { if (v == par) { continue; } dfs(v, u); sz[u] += sz[v]; } } int ans[1000005], fa[1000005]; void solve() { int n; std::cin >> n; for (int i = 1; i <= n - 1; i++) { int u, v; std::cin >> u >> v; G[u].emplace_back(v); G[v].emplace_back(u); edge[i].u = u, edge[i].v = v; fa[u] = v; } int root = 1; for (int i = 1; i <= n; i++) { if (!fa[i]) { root = i; } } dfs(root, 0); int res = 1e9; for (int i = 1; i <= n - 1; i++) { int u = edge[i].u, father = edge[i].v; int now = std::abs(sz[u] - (n - sz[u])); ans[now]++; res = std::min(res, now); } std::cout << res << ' ' << ans[res] << '\n'; } int main() { std::cin.tie(nullptr) -> sync_with_stdio(false); int T = 1; //std::cin >> T; while (T--) { solve(); } return 0; } /* 平衡 时间限制: 1000MS 内存限制: 65536KB 题目描述: 输入一棵树 T,你需要删除一条边,这棵树会被分成A 和 B 两棵树。你需要让两部分的节点数的差的绝对值| |A|-|B| |尽可能小。输出最小的| |A|-|B| |和最优方案的数量。 输入描述 第一行一个整数 n表示节点的数量,节点从1 到 n编号。 接下来n-1行每行两个正整数 s t,表示s的父亲是t。 输入保证是一棵树。 对于所有数据 1<=n<=100000。 输出描述 输出一行,两个整数,用空格分开,分别表示最优解和最优方案数。 样例输入 3 2 1 3 1 样例输出 1 2 */
B. 配制溶液
要配置体积为V的溶液,可以枚举子状态 i 和 V - i, 其实就是求子状态的最优策略,所以可以递归做,类似于记忆化搜索。
/* 小红书 23届补录&24届实习 【24届实习】算法笔试 */ #include<bits/stdc++.h> #define int long long #define debug(x) std::cerr << x << '\n'; #define all(x) x.begin(), x.end() using i64 = long long; using pii = std::pair<int, int>; using vi = std::vector<int>; using vp = std::vector<pii>; using vl = std::vector<i64>; int A[1005], B[1005]; int dp[1005][1005]; int Max[1005], f[1005]; int n, X, C; int dfs(int now) { if (f[now] != -1) { return f[now]; } int ans = 0; for (int i = 1; i <= now - 1; i++) { if (!dfs(i) or !dfs(now - i)) { continue; } if (i == now - i) { ans = std::max(ans, dfs(i) + dfs(now - i) + X); } else { ans = std::max(ans, dfs(i) + dfs(now - i)); } } return f[now] = std::max(Max[now], ans); } void solve() { memset(f, -1, sizeof(f)); std::cin >> n >> X >> C; for (int i = 1; i <= n; i++) { std::cin >> A[i]; } for (int i = 1; i <= n; i++) { std::cin >> B[i]; Max[A[i]] = std::max(Max[A[i]], B[i]); } for (int i = 1; i <= C; i++) { std::cerr << i << ' ' << dfs(i) << '\n'; } std::cout << dfs(C) << '\n'; } signed main() { std::cin.tie(nullptr) -> sync_with_stdio(false); int T = 1; //std::cin >> T; while (T--) { solve(); } return 0; } /* 配制溶液 时间限制: 3000MS 内存限制: 589824KB 题目描述: 实验室需要配制一种溶液。现在,研究员面前有n种该物质的溶液,每一种有无限多瓶,第i种的溶液体积为xi,里面含有yi单位的该物质。研究员每次可以选择一瓶溶液,将其倒入另外一瓶(假设瓶子的容量无限),即可以看作将两个瓶子内的溶液合并。此时合并的溶液体积和物质含量都等于之前两个瓶子内的之和。 特别地,如果瓶子A与B的溶液体积相同,那么A与B合并之后该物质的含量会产生化学反应,使得该物质含量增加X单位。 研究员的任务是配制溶液体积恰好等于C的,且尽量浓的溶液(即物质含量尽量多)。研究员想要知道物质含量最多是多少。 输入描述 第一行三个正整数n,X,C; 第二行n个正整数x1,x2,...,xn,中间用空格隔开; 第三行n个正整数y1,y2,...,yn,中间用空格隔开。 对于所有数据,1≤n,X,C,yi≤1000,1≤xi≤C 数据保证至少存在一种方案能够配制溶液体积恰好等于C的溶液。 输出描述 输出一行一个整数,表示答案。 样例输入 3 4 16 5 3 4 2 4 1 样例输出 29 */
C. 双色球
有个小坑:当红球在袋子里的时候,每过去一秒上面的数字就会增加1,蓝球上的数字则会减少1。
一开始没注意到,wa了很多次。我们只需要记录当前放了多少个红和蓝,记录编号为id的小球放进去的时间就可以了。
/* 小红书 23届补录&24届实习 【24届实习】算法笔试 */ #include<bits/stdc++.h> #define int long long #define debug(x) std::cerr << x << '\n'; #define all(x) x.begin(), x.end() using i64 = long long; using pii = std::pair<int, int>; using vi = std::vector<int>; using vp = std::vector<pii>; using vl = std::vector<i64>; int A[500005], B[1000005], C[1000005], in[1000005]; std::string color; void solve() { int n; while (std::cin >> n) { //std::cin >> n; i64 sum = 0; int red = 0, blue = 0; for (int i = 1; i <= n; i++) { std::cin >> A[i]; } std::cin >> color; color = ' ' + color; int m; std::cin >> m; for (int i = 1; i <= m; i++) { std::cin >> B[i]; } for (int i = 1; i <= m; i++) { std::cin >> C[i]; } for (int i = 2; i <= m; i++) { assert(B[i] > B[i - 1]); } int Nowtime = 0; memset(in, -1, sizeof(in)); std::vector<i64> ans; for (int i = 1; i <= m; i++) { int TimeDet = B[i] - Nowtime; sum += red * TimeDet; sum -= blue * TimeDet; if (C[i] == 0) { ans.emplace_back(sum); } else { int id = std::abs(C[i]); if (C[i] > 0) { assert(in[id] == -1); sum += A[id]; in[id] = B[i]; red += color[id] == 'R'; blue += color[id] == 'B'; } else if (C[i] < 0) { sum -= A[id]; sum -= (color[id] == 'R' ? 1 : -1) * (B[i] - in[id]); A[id] = (A[id] + (color[id] == 'R' ? 1 : -1) * (B[i] - in[id])); red -= color[id] == 'R'; blue -= color[id] == 'B'; in[id] = -1; } } //std::cerr << i << ' ' << sum << '\n'; Nowtime = B[i]; } std::cout << ans.size(); for (auto iter : ans) { std::cout << ' ' << iter; } std::cout << '\n'; } } signed main() { std::cin.tie(nullptr) -> sync_with_stdio(false); int T = 1; //std::cin >> T; while (T--) { solve(); } return 0; } /* 时间限制: 3000MS 内存限制: 589824KB 题目描述: 小明有一个神奇的袋子和一堆编号为1到n的球。每个球都被涂成了红色或者蓝色,且这些球的表面上都分别写着一个数字。 当红球在袋子里的时候,每过去一秒上面的数字就会增加1,蓝球上的数字则会减少1。 小明会时不时向袋子里放入球或取出球,并且他想知道某些时刻袋子中球上写的数字之和。 输入描述 第一行有一个正整数n(1<=n<=5000),代表小明有几个球。 第二行有n个范围在[-1000000,1000000]内的整数,第i个代表编号为1到n的球上写的数。 第三行是一个长度为n的仅由`R`和`B`构成的字符串,第i个字母代表编号为i的球是红色(R)或蓝色(B) 第四行有一个正整数m(1<=m<=100000),代表小明进行了几次操作。 第五行有m个递增的正整数,第i个代表小明进行的第i次操作时间点。每个时间点小明只会进行至多一次操作。时间点的范围在[1,1000000000]内。 第六行有m个整数,第i个代表小明进行的操作。0为询问当前时间点袋中球上的数字之和,正数x代表放入了编号为x的球,负数-x代表取出了编号为x的球。 最开始袋子是空的。 你可以认为球上的数字变化均发生在时间点之前,而每次操作均发生在时间点之后。输入保证操作合法。 输出描述 设小明进行了k次询问。你需要在一行中先输出k,然后输出k个数,第i个代表第i次询问的答案。 题目保证小明进行过至少一次询问。 样例输入 3 -5 4 6 RBR 9 1 2 3 4 5 6 8 13 14 1 3 0 2 -3 0 -1 0 -2 样例输出 3 4 2 -5 3 1 1 1 RRB 4 1 2 3 10 1 3 -1 0 */#小红书##小红书实习招聘##小红书笔试##软件开发2023笔面经##小红书信息集散地#
一些比赛的题解 文章被收录于专栏
一些比赛的题解