美团笔试 美团实习 0401
笔试时间:2023年04月01日
第一题
题目:算数
小美在数学课上学会了加减乘除,现在她想多进行一些算数训练来加强自己的计算能力。为了不重复出题,她想出一个好方法。她先写下了一排n个数(n≥2),依次用加号连接。举例来说,小美可能写下了如下的式子1+4+7+4+2+3+1共7个数以及6个加号。接着小美以一种全新的方式进行出题:她每次选择一个加号,将它改变成加减乘除中的一个(虽然很奇怪,但小美认为加号也可以被改成加号,尽管不会产生任何影响),然后计算整个式子的结果。值得注意的是,小美认为每次操作不对后续操作产生影响,详见样例解释。小美认真做了很多次算数训练,现在她想让作为她好朋友的你帮她用程序计算一次,方便她核对答案。
输入描述
第一行一个整数n,含义见题面。
接下来一行n个整数a1,a2,..,an,依次表示小美初始写下的连加算式中的每一个数。
接下来一个整数m,表示小美做了m次算数训练
接下来2m个以空格分开数字或符号 t1,o1, t2,o2, ... tm,om,其中ti为数字,oi是'+','-','*','/'(即加减乘除符号,不含引号)中的一个符号,表示第 i 次操作选定了第ti个加号,将其改变为了oi。
对于所有的的数据,2≤N≤50000,1≤M≤50000,1≤ai≤500,1≤ti<N,oi∈{+,-,*,/}
输出描述
输出一行m个整数,分别表示每次操作的答案,结果四舍五入到一位小数。
样例输入
5
1 2 4 2 5
3
1 - 2 * 4 /
样例输出
10.0 16.0 7.4
解释:
第一次操作后算数式为1-2+4+2+5 = 10.0
第二次操作后算数式为1+2*4+2+5 = 16.0
第三次操作后算数式为1+2+4+2/5 = 7.4
值得注意的是,每次操作都认为对初始的全加号式子(此处为1+2+4+2+5)进行操作,操作之间互不影响。
参考题解
按照题意模拟。
C++:[此代码未进行大量数据的测试,仅供参考]
#include <iostream> #include <vector> #include <iomanip> using namespace std; double perform_operation(int a, int b, char op) { switch (op) { case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': return static_cast<double>(a) / b; default: return 0; } } int main() { int n; cin >> n; vector<int> nums(n); for (int i = 0; i < n; ++i) { cin >> nums[i]; } // 计算原始算式的结果 int original_sum = 0; for (int i = 0; i < n; ++i) { original_sum += nums[i]; } int m; cin >> m; vector<int> t(m); vector<char> op(m); for (int i = 0; i < m; ++i) { cin >> t[i] >> op[i]; } cout << fixed << setprecision(1); for (int i = 0; i < m; ++i) { int index = t[i] - 1; char operation = op[i]; double result = original_sum - nums[index] - nums[index + 1]; result += perform_operation(nums[index], nums[index + 1], operation); cout << result << " "; } return 0; }
Java:[此代码未进行大量数据的测试,仅供参考]
import java.util.Scanner; public class Main { public static double performOperation(int a, int b, char op) { switch (op) { case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': return (double) a / b; default: return 0.0; } } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int[] nums = new int[n]; for (int i = 0; i < n; ++i) { nums[i] = scanner.nextInt(); } // 计算原始算式的结果 int originalSum = 0; for (int i = 0; i < n; ++i) { originalSum += nums[i]; } int m = scanner.nextInt(); int[] t = new int[m]; char[] op = new char[m]; for (int i = 0; i < m; ++i) { t[i] = scanner.nextInt(); op[i] = scanner.next().charAt(0); } for (int i = 0; i < m; ++i) { int index = t[i] - 1; char operation = op[i]; double result = originalSum - nums[index] - nums[index + 1]; result += performOperation(nums[index], nums[index + 1], operation); System.out.printf("%.1f ", result); } } }
Python:[此代码未进行大量数据的测试,仅供参考]
def perform_operation(a, b, op): if op == '+': return a + b elif op == '-': return a - b elif op == '*': return a * b elif op == '/': return float(a) / b else: return 0.0 def main(): n = int(input()) nums = list(map(int, input().split())) # 计算原始算式的结果 original_sum = sum(nums) m = int(input()) t = [] op = [] for _ in range(m): ti, opi = input().split() t.append(int(ti)) op.append(opi) for i in range(m): index = t[i] - 1 operation = op[i] result = original_sum - nums[index] - nums[index + 1] result += perform_operation(nums[index], nums[index + 1], operation) print("{:.1f}".format(result), end=" ") if __name__ == "__main__": main()
第二题
题目:整理
小美正在整理桌子上的一排装饰品。小美对待装饰品摆放方式的审美角度很奇特,她认为高度相差比较大的装饰品放在相邻位置会很难看,她想对这一排装饰品进行整理,可以交换任意两个装饰品的位置任意多次。假设当前从左到右n个装饰品的高度分别为h1,h2,...,hn,那么当前一排装饰品的丑陋值为,其中|x|为x的绝对值。小美想最小化她的装饰品的丑陋值,请你帮她排一下顺序。形式化地来讲,有一长为n的序列a1,a2,...,an,你可以任意次数地进行交换,每次交换都可以选择任意两个不同的数i,j,交换ai,aj的位置。经过若干次交换后,序列变为h1,h2,...,hn,其丑陋值为
,你需要找出一种交换方式,使得最终序列{hn}的丑陋值最小化。你不需要输出具体交换方式,只需要输出最终的{hn}序列的丑陋值即可。
输入描述
第一行一个整数n,表示小美的装饰品数量。
接下来一行n个整数a1,a2,...,an,依次表示从左到右n个装饰品的高度。
对于所有的数据:2≤N≤50000,0≤ai≤10^9。
输出描述
输出第一行一个数,为最优方案的最小丑陋值。
样例输入
3 3 1 2
样例输出
2
提示:
我们可以将3和1交换,得到1 3 2然后再将2和3交换,得到1 2 3可以证明,此时有最小丑陋值|1-2|+|2-3|=2
参考题解
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]; } sort(a.begin(), a.end()); int res = 0; for (int i = 0; i < n - 1; ++i) { res += a[i + 1] - a[i]; } cout << res << endl; return 0; }
Java:[此代码未进行大量数据的测试,仅供参考]
import java.util.Arrays; import java.util.Scanner; public class Main { 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(); } Arrays.sort(a); int res = 0; for (int i = 0; i < n - 1; ++i) { res += a[i + 1] - a[i]; } System.out.println(res); } }
Python:[此代码未进行大量数据的测试,仅供参考]
n = int(input()) a = [int(c) for c in input().split(" ")] a.sort() res = 0 for i in range(n-1): res += a[i+1] - a[i] print(res)
第三题
题目:收藏家
小美爱上了收藏!现在她给自己修建了一排n个收藏夹分别编号为1,2,3,...,n。有时小美会改变某一个收藏夹里的内容,例如从中拿入、拿出一些藏品,这样的操作会改变小美对这个收藏夹的欣赏程度,我们记编号为i的收藏夹小美对其的欣赏程度为ai。还有一些时候,小美会欣赏连续编号的一些收藏夹,例如编号为L,L+1,L+2,...,R-1,R的这一些收藏夹,她能从中获得的满足感为这些收藏夹的欣赏程度之和,即,小美想在欣赏之前提前做一些评估,想知道如果她选择编号区间为[L,R]的收藏夹,能给她带来的满足感是多少。小美想请你,最好的朋友,来帮帮她。
输入描述
第一行两个整数n和m,表示小美的收藏夹数量和小美的操作数量。初始时刻收藏夹都是空的,也即ai=0(i∈[1,n]);
第二行m个整数op1,op2,...,opm;
第三行m个整数x1,x2,...,xm;
第四行m个整数y1,y2,...,ym,这些共同表示了m次操作。具体而言,对第i次操作,opi=0时表示为一次收藏夹更新操作,会将xi位置的收藏夹欣赏程度更新为yi,即axi=yi;opi=1时表示为一次查询操作,表示如果小美欣赏编号在区间[xi,yi]的收藏夹,能获得的满足感是多少,也即的值;
对于所有的数据,1≤n,m≤ 50000,opi∈{0,1},当opi=0 时,1≤xi≤n,0≤yi≤10000; 当opi=1 时,1≤xi≤yi≤n。保证至少有一次opi=1 的操作。
输出描述
对每个opi=1的操作,输出一个数表示对应答案。空格隔开所有答案。
样例输入
4 7
1 0 1 0 1 0 1
1 1 1 3 1 4 1
3 2 3 5 3 100 3
样例输出
0 2 7 7
参考题解
参考线性树做法。
Python:[此代码未进行大量数据的测试,仅供参考]
class Node: def __init__(self, left=None, right=None, left_child=None, right_child=None, value=None): self.left = left self.right = right self.left_child = left_child self.right_child = right_child self.value = value class SegmentTree: def __init__(self, nums): n = len(nums) self.root = Node() self.nums = [0] + nums self.build_tree(self.root, 1, n) def build_tree(self, node, left, right): if left == right: node.value = self.nums[left] return mid = (left + right) // 2 if node.left_child is None: node.left_child = Node() if node.right_child is None: node.right_child = Node() self.build_tree(node.left_child, left, mid) self.build_tree(node.right_child, mid + 1, right) node.value = node.left_child.value + node.right_child.value def update(self, node, left, right, index, value): node.value += value if left == right: return mid = (left + right) // 2 if index <= mid: self.update(node.left_child, left, mid, index, value) else: self.update(node.right_child, mid + 1, right, index, value) def query(self, node, left, right, start, end): if left == start and end == right: return node.value mid = (left + right) // 2 if end <= mid: return self.query(node.left_child, left, mid, start, end) elif start > mid: return self.query(node.right_child, mid + 1, right, start, end) left_part = self.query(node.left_child, left, mid, start, mid) right_part = self.query(node.right_child, mid + 1, right, mid + 1, end) return left_part + right_part n, m = map(int, input().split()) ops = [int(c) for c in input().split()] xs = [int(c) for c in input().split()] ys = [int(c) for c in input().split()] nums = [0 for _ in range(n)] st = SegmentTree(nums) for i in range(m): op, x, y = ops[i], xs[i], ys[i] if op == 0: delta = y - nums[x - 1] nums[x - 1] = y st.update(st.root, 1, len(nums), x, delta) else: print(st.query(st.root, 1, len(nums), x, y), end=" ")
第四题
题目:倒水魔法
小美最近在魔法课中掌握了倒水魔法:可以运用法力隔空倒水。最近魔法课考试临近,小美早早地来到了魔法训练室训练难以掌握的倒水魔法。魔法训练室里有n个神奇的杯子,有着不同的大小,假设第i个杯子已满,向其倒水,多余的水会正正好好流向第i+1个杯子(如果i=n时没有下一个杯子,不会有杯子接住此时多余的水而溢出到魔法训练室的水池)。这些杯子有着初始固定的水量,每次练习后杯子里的水都会还原到最初状态。每次练习时,魔法黑板会告诉小美需要将哪一个杯子倒满水。因为每个杯子的材质和形状有所不同,所以对其释放倒水魔法需要消耗的魔法值不同。小美想尽可能多练习,所以需要最小化每次消耗的魔法值的总量。
输入描述
第一行一个整数n,表示杯子数量;
第二行n个整数x1,x2,...,xn,依次表示第 i 个杯子能容纳水的量(单位为毫升);
第三行n个整数y1,y2,...,yn,依次表示第 i 个杯子初始有的水量(单位为毫升);
第四行n个整数z1,z2,...,zn,依次表示对第 i 个杯子每添加1毫升水需要消耗的法力值;
第五行一个整数m,表示练习的数量;
第六行m个整数q1,q2,...,qm,依次表示第i次练习时需要将第qi个杯子倒满。(每次练习过后,杯子里的水量都会还原为初始状态,不会影响到下一次练习);
1≤n,m≤3000 , 1≤yi≤xi≤109, 1≤zi≤300,1≤qi≤n
输出描述
输出第一行m个数,依次表示每次训练时需要消耗的最小法力值。如果杯子初始本身就是满的,则需要消耗的法力值为0。
样例输入
3
1 2 3
1 1 2
1 2 5
2
3 1
样例输出
2 0
提示:
第一次训练,最优方案如下:
初始时杯子的水量和最大容量分别为: 1/1 1/2 2/3
1、给1号杯子(本身已满)倒水1毫升,消耗1点法力,水溢出转移到2号杯子,当前状态为: 1/1 2/2 2/3
2. 继续给1号杯子(本身已满)倒水1毫升,消耗1点法力,水溢出到2号杯子(也已满),继续溢出到3号杯子,此时3号杯子也被成功注满水,状态为:1/1 2/2 3/3。共消耗2点法力。可以证明没有更优方案。
第二次训练时,:
初始时杯子的水量和最大容量分别为(注意不同训练互不影响,因为训练结束后魔法会让水杯还原为初始状态):1/1 1/2 2/3
可以发现1号杯子已满,不用注水,消耗法力为0。
参考题解
C++:[此代码未进行大量数据的测试,仅供参考]
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { int n; cin >> n; vector<int> xs(n), ys(n), zs(n); for (int i = 0; i < n; ++i) { cin >> xs[i]; } for (int i = 0; i < n; ++i) { cin >> ys[i]; } for (int i = 0; i < n; ++i) { cin >> zs[i]; } int m; cin >> m; vector<int> qs(m); for (int i = 0; i < m; ++i) { cin >> qs[i]; } vector<int> pres(n + 1, 0); for (int i = 1; i <= n; ++i) { pres[i] = pres[i - 1] + xs[i - 1] - ys[i - 1]; } for (int i = 0; i < m; ++i) { int q = qs[i]; int cur = INT_MAX; for (int j = 0; j < q; ++j) { cur = min(cur, (pres[q] - pres[j]) * zs[j]); } cout << cur << " "; } cout << endl; return 0; }
Java:[此代码未进行大量数据的测试,仅供参考]
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int[] xs = new int[n]; int[] ys = new int[n]; int[] zs = new int[n]; for (int i = 0; i < n; ++i) { xs[i] = scanner.nextInt(); } for (int i = 0; i < n; ++i) { ys[i] = scanner.nextInt(); } for (int i = 0; i < n; ++i) { zs[i] = scanner.nextInt(); } int m = scanner.nextInt(); int[] qs = new int[m]; for (int i = 0; i < m; ++i) { qs[i] = scanner.nextInt(); } int[] pres = new int[n + 1]; for (int i = 1; i <= n; ++i) { pres[i] = pres[i - 1] + xs[i - 1] - ys[i - 1]; } for (int i = 0; i < m; ++i) { int q = qs[i]; int cur = Integer.MAX_VALUE; for (int j = 0; j < q; ++j) { cur = Math.min(cur, (pres[q] - pres[j]) * zs[j]); } System.out.print(cur + " "); } System.out.println(); } }
Python:[此代码未进行大量数据的测试,仅供参考]
n = int(input()) xs = [int(c) for c in input().split(" ")] ys = [int(c) for c in input().split(" ")] zs = [int(c) for c in input().split(" ")] m = int(input()) qs = [int(c) for c in input().split(" ")] pres = [0 for _ in range(n + 1)] for i in range(1,n+1): pres[i] += pres[i-1] + xs[i-1] - ys[i-1] for i in range(m): q = qs[i] cur = float('inf') for j in range(q): cur = min(cur, (pres[q] - pres[j]) * zs[j]) print(cur, end= " ")
第五题
题目:价值
你现在有一棵树,树上的每个节点都有自己的价值。价值的计算规则如下所示:
1、若某节点N没有儿子节点,那么节点N价值为1;
2、若某节点N有两个儿子节点,那么节点N价值为两个儿子节点的价值之和,或者价值之按位异或。这取决于节点N的颜色,若N的颜色为红色,那么节点N价值为两个儿子节点的价值之和;若N的颜色为绿色,那么节点N价值为两个儿子节点的价值之按位异或。保证这棵树要么没有儿子节点,要么有两个儿子节点。注:树,是一种无向无环联通图。
按位运算就是基于整数的二进制表示进行的运算。按位异或用符号⊕表示,两个对应位不同时为1,否则为0。
如:
5=(101)2
6=(110)2
5⊕6=3,即 (101)2 ⊕ (110)2 = (011)2
输入描述
第一行一个正整数n表示节点个数。
第二行n-1个正整数p[i](2≤i≤n)表示第 i 个节点的父亲。1号节点是根节点。
第三行n个整数c[i](1≤i≤n),当c[i] = 1时表示第 i 个节点是红色,c[i] = 2则表示绿色。
数据保证形成合法的树。
对于所有的数据,n≤50000
输出描述
输出一行一个整数表示根节点的值。
样例输入
3
1 1
2 2 2
样例输出
0
参考题解
Python:[此代码未进行大量数据的测试,仅供参考]
n = int(input()) parent_nodes = [int(c) for c in input().split()] colors = [int(c) for c in input().split()] class TreeNode: def __init__(self, color=0): self.color = color self.children = [] tree_nodes = [TreeNode() for i in range(n)] for i in range(n): tree_nodes[i].color = colors[i] for i in range(n - 1): tree_nodes[parent_nodes[i] - 1].children.append(tree_nodes[i + 1]) def dfs(node): if not node.children: return 1 values = [] for child in node.children: values.append(dfs(child)) if node.color == 1: return sum(values) current = 0 for value in values: current ^= value return current print(dfs(tree_nodes[0]))#美团笔试##美团实习#