美团秋招第三场8.24笔试-Java后端
编程题(20 + 20 + 30)
(1)给定 二维平面上的起点和终点,再给定 n 个金币位置,需要从起点出发,将所有金币都带到终点,每次只能带一个金币,求最短路程。
做法:不考虑起点时,总花费为每个金币到终点距离*2, 然后再枚举第一个去的金币位置(总花费 + 起点到金币距离 - 终点到金币距离),答案取min。
(2)给定三个数字a, b, c 和一个 k, 有 k 次操作将其中一个数字加上1,问a * b * c的最大值
做法:将abc尽量凑到相近就行了(每次给最小的数加),注意取模
(3)给定 n 个物品,每个物品有 0, 1两种类型以及价值,q 次询问 {l, r, t, k} 表示购买下标 [l, r] 内中类型为 t 的物品,其中价值最大的 k 个,输出购买的物品下标,不够时输出 -1。(操作之间是继承关系,非独立)
数据范围:1 <= n, q <= 1e5;
做法:题目给了两秒,一眼就是线段树板子了,但是还要维护区间不同种类物品最大值,比较麻烦,当时被第二题取模卡了太久,这题只剩20分钟写,也来不及码线段树,于是突然想到可以用并查集维护区间合并问题,常数还小,还好写。
具体来说,在执行购买操作时,假设要将第 i 个物品买掉, 那么可以将 i 的 father 指向 i + 1,也就是merge(i, i + 1), 这样在遍历物品时,下一个还没被购买的物品就是 i + 1的father, 均摊几乎为 O(1) 复杂度。事实证明跑的很快
总复杂度:O(nlogn)
********更正一下***************
这题的查询如果全部是1~n区间查询,k = 1,那该复杂度会被卡成n^2logn, 事后想起来这题应该是随机数据被我卡过去了
上代码:
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int a[N], b[N], n, q, fa[N]; int f(int x) { while(fa[x] != x) x = fa[x] = fa[fa[x]]; return fa[x]; } void merge(int x, int y) { int fx = f(x), fy = f(y); fa[fx] = fa[fy]; } signed main() { cin >> n >> q; for(int i = 1; i <= n; i ++) cin >> a[i], fa[i] = i; for(int i = 1; i <= n; i ++) cin >> b[i]; fa[n + 1] = n + 1; while(q --) { int l, r, t, k; cin >> l >> r >> t >> k; vector<pair<int, int>> p; for(int i = l; i <= r; i = f(++ i)) { if(b[i] == t) { p.push_back({a[i], i}); } } sort(begin(p), end(p), [&](pair<int, int> a, pair<int, int> b) -> bool { if(a.first != b.first) return a.first > b.first; return a.second < b.second; }); for(int i = 0; i < k && i < p.size(); i ++) { cout << p[i].second << ' '; b[p[i].second] = -1; merge(p[i].second, p[i].second + 1); } if(k > p.size()) cout << -1; cout << endl; } }
总结: 上次笔试考了线段树,这次压轴又来,还好想到了避开线段树的写法,不然时间也不够,第二题的数据范围给的很大,取模不知道哪里出了调了好久还是没满分
最后得分:100%、 65%、 100%
#美团##美团求职进展汇总##美团笔试##笔试#