美团 秋招 2023.08.19 机试题目与题解
1、小美的外卖订单编号
美团商家的订单发起时,订单编号最开始从\(1\)开始,后续每发起一个订单,订单编号便在上一订单编号的基础上\(+1\)。为了防止订单号过大,商家还可以设置一个编号上限\(m\),当订单编号超过\(m\)时,将又从\(1\)开始编号。
小美想知道,当订单编号上限为\(m\)时,第\(x\)个订单编号是多少?将有\(q\)次询问。
输入
第一行输入一个整数\(q(1 \leq q \leq 50000)\)。
接下来\(q\)行,每行两个整数\(m,x(1 \leq m,x \leq 10^9)\)。
输出
\(q\)行,每行一个整数表示答案。
样例
1 | 输入: |
解答
可见,订单号都是按照\(1,2,3,\dots,m,1,2,3,\dots,m,1,\dots\)的循环往复,只需要使用模运算即可。对于第\(x\)个订单,它的编号将是\((x-1)\% m+1\)。
代码
1 |
|
2、小美的加法
小美有一个长度为\(n\)的数组,她想将这个数组进行求和,即\(sum = a_1+a_2+...+a_n\)。
小美可以使用一次魔法(也可以不使用),将其中一个加号变成乘号,使得\(sum\)最大。
求出最大的\(sum\)。
输入
第一行输入一个整数 \(n\) 。 第二行输入 \(n\) 个整数表示数组 \(a\) 。
- \(1 \leq n \leq 10^5\)
- \(1 \leq a_i \leq 10^9\)
输出
输出一个整数表示答案。
样例
1 | 输入: |
解答
假设现在将\(a[i]\)和\(a[i+1]\)中间的加号替换成乘号,式子中的其它符号不变,那么考虑如下变化:
- \(a[i]\)和\(a[i+1]\)这两个项从这个加法式子中剔除。
- \(a[i]\times a[i+1]\)作为新的项被添加进来。
因此,这个式子的新值就变成了\(sum-a[i]-a[i+1]+a[i]\times a[i+1]\)。最终答案为
\[\max\left\{sum,\max_{i=1}^{n-1}\{sum-a[i]-a[i+1]+a[i]\times a[i+1]\}\right\}\]
代码
1 |
|
3、小美的01串翻转
小美定义一个 01 串的权值为:每次操作选择一位取反,使得相邻字符都不相等的最小操作次数。
例如,"10001"的权值是 1,因为只需要修改一次:对第三个字符取反即可。 现在小美拿到了一个 01 串,她希望你求出所有非空连续子串的权值之和,你能帮帮她吗?
输入
一个仅包含'0'和'1'的字符串,长度不超过\(2000\)。
输出
所有非空子串的权值和。
样例
1 | 输入: |
解答
一个01字符串,且相邻两位都不相同的无非就以下两种情况:
10101010...
01010101...
因此,对于一个长度为\(n\)的01字符串\(s\),它的权值就是转换成这两个字符串中较少的次数。
这意味着字符串的下标奇偶性都和字符的奇偶性要么都相同,要么都不相同。假设字符串\(s\)有\(c\)个下标的奇偶性都和字符的奇偶性相同,那么\(s\)的权值为\(\min\{c,n-c\}\)。直接枚举每个起点,并维护\(c\)值即可。
代码
1 |
|
4、小美的数组构造
小美拿到了一个数组\(a\),她准备构造一个数组\(b\)满足:
- \(b\)的每一位都和\(a\)对应位置不同,即\(b_i \neq a_i\)
- \(b\)的所有元素之和都和\(a\)相同。
- \(b\)的数组均为正整数。
请你告诉小美有多少种构造方式。由于答案过大,请对\(10^9+7\)取模。
输入
第一行输入一个正整数\(n\),代表数组的大小。
第二行输入\(n\)个正整数\(a_i\),代表小美拿到的数组。
- \(1\leq n \leq 100\)
- \(1\leq a_i \leq 300\)
- \(1\leq \sum a_i \leq 500\)
输出
一个整数,代表构造方式对\(10^9+7\)取模的值。
样例
1 | 输入: |
解答
这是一道特别特别裸的动态规划的题目,状态设计非常明显:令状态\(f(i,j)\)表示有多少个数组\(b\)满足如下条件:
- 其长度为\(i\),和为\(j\)。
- \(\forall p\in\{1,2,\dots,i\}\),都有\(b_i\neq a_i\)。
那么可以写出如下状态转移方程:
\(f(i,j)= \left \{\begin{aligned} &1 & & \text{if}\quad i=0\land j=0 \\ &0 & & \text{if}\quad i=0\land j\neq 0 \\ &\sum_{1\le k\le j,k\neq a_i} f(i-1,j-k) & & \text{if}\quad i>0\\ \end{aligned}\right.\)
这个方程的前两行说明如果一个数组是空数组,那么其和为\(0\)。第三行假定了\(b_i\)可以填上任意不超过\(j\)的整数\(k\),并且不能填上\(a_i\),填上这个数\(k\)后,状态从\(f(i-1,j-k)\)转移到\(f(i,j)\)。
因此,最终答案为\(f(n,s)\),其中\(\displaystyle{s=\sum_{i=1}^n a_i}\)。
代码
1 |
|
5、小美的数组操作
小美拿到了一个数组,她每次可以进行如下操作:
选择两个元素,一个加 \(1\),另一个减 \(1\)。
小美希望若干次操作后,众数的出现次数尽可能多。你能帮她求出最小的操作次数吗?
众数定义:在一组数据中,出现次数最多的数据,是一组数据中的原数据,而不是相应的次数。 一组数据中的众数不止一个,如数据2、3、-1、2、1、3中,2、3都出现了两次,它们都是这组数据中的众数。
输入
第一行为一个正整数\(n\),代表数组的大小。 第二行输入\(n\)个正整数\(a_i\),代表小美拿到的数组。
- \(1\leq n \leq 10^5\)
- \(1\leq a_i \leq 10^9\)
输出
一个整数,代表最小的操作次数。
样例
1 | 输入: |
解答
令数组和\(\displaystyle{s=\sum_{i=1}^n a_i}\)。如果\(s\)是\(n\)的倍数,那么说明存在一种操作,可以使得众数的出现次数为\(n\),这时得到的是\(a\)的平均数,只需要将所有值平均化即可。因此,令平均数\(\overline{a}=s/n\),那么最终答案是\(\displaystyle{s=\sum_{1\le i\le n,a_i>\overline{a}} (a_i-\overline{a})}\)。
如果\(s\)不是\(n\)的倍数,那么必定存在一种方法可以使得众数的出现次数为\(n-1\)。具体操作是假设众数值是\(x\),选定\(n-1\)个数,尝试将它们全部变成\(x\),并且将多余的加\(1\)或者是减\(1\)操作未被选上的那\(1\)个元素。
因此,令这\(n-1\)个元素所构成的数组为\(b\),如果现在要将\(b\)中的数都变成\(x\),那么令\(\displaystyle{s_1(x)=\sum_{1\le i< n,b_i>x} (b_i-x),s_2(x)=\sum_{1\le i< n,b_i<x} (x-b_i)}\)。那么所需要的操作个数为\(f_(x)=\max\{s_1(x),s_2(x)\}\),因为有些加\(1\)操作可以和减\(1\)操作共同处在同一个操作中,只花费一个次数。
假设\(m,M\)分别表示\(b\)中的最小值和最大值,那么可见\(s_1(x)\)在区间\([m,M]\)是一个递减函数,\(s_2(x)\)在区间\([m,M]\)是一个递增函数。因此\(f(x)\)是一个向下凹陷的函数,我们只需要找到函数\(f(x)\)的最低点即可。
在这里我使用的是二分法进行这个最低点,具体是找到最小的\(x\)使得\(f(x)<f(x+1)\),那么\(x\)就是最低点。当然使用三分法也可以,具体不在这里讲述三分法。
最后一个问题:最终如何选择\(a\)中的\(n-1\)个数成为\(b\)?在这里我是直接用最小的\(n-1\)个数或者是最大的\(n-1\)个数,因为它们彼此更加接近,最优解必定在它们之间产生。
代码
1 |
|