美团2024届秋招笔试第一场编程真题
1.小美的外卖订单
题目描述
小美正在设计美团外卖的定价信息。已知外卖定价的规则如下:
- 每道菜有折扣价和原价。折扣价不能超过原价。
- 订单有满𝑥x元减𝑦y元的优惠。当购买的菜的价格总和不小于𝑥x元时,总价格可以减𝑦y元。“减”的价格不能超过“满”的价格。
- 满减优惠和折扣价是互斥的,当且仅当每个菜都选择了原价才可以触发满减。
- 系统会自动为客户计算最低价格的方案。
在设计定价时,原价、折扣价和满减的价格都必须是正实数。如果设计的定价发生问题,则会提示数据错误。
请使用等价划分法设计测试用例,来测试该系统的功能。
分析
- 求出所有物品的原价和折扣价
- 如果原价满足满减要求,求出满减在之后的价格
- 输出折扣价和满减之后的价格的最小值
代码
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
//输入物品总数
int n;
cin >> n;
//判断物品总数是否合法
if (n < 0) cout << "error" << endl;
double yuanjia = 0;//原价
double zhekoujia = 0;//折扣价
while (n > 0) {
//输入原价和折扣价
double a, b;
cin >> a;
cin >> b;
if (a <= 0 || b <= 0) {
cout << "error" << endl;
return 0;
}
if (a < b) {
cout << "error" << endl;
return 0;
}
n--;
//计算原价和 以及 折扣价和
yuanjia += a;
zhekoujia += b;
}
//输入满减要求
double x, y;
cin >> x >> y;
if (x <= 0 || y <= 0) {
cout << "error" << endl;
return 0;
}
if (x < y) {
cout << "error" << endl;
return 0;
}
double manjian = 0;
//满足满减要求,求出满减价格
//最后输出满减价格 和 折扣价 的最小值
if (yuanjia >= x) {
manjian = yuanjia - y;
if (manjian < zhekoujia) {
cout << manjian << endl;
return 0;
}
}
printf("%.2lf\n", zhekoujia);
//cout<<zhekoujia<<endl;
return 0;
}
2.小美的字符串匹配度
题目描述
小美有两个长度为n只包含小写字母的字符串s和t,小美定义“两个字符串的匹配度”为𝑖∈[1,𝑛]中𝑠[𝑖]=𝑡[𝑖]的数量,例如"abacd"和"aabdd"的匹配度就是2。
现在你可以进行最多一次以下操作:
对于字符串t,选择两个索引𝑖,𝑗(1≤𝑖<𝑗≤𝑛)i,j(1≤i<j≤n),交换𝑡[𝑖]和𝑡[𝑗]。
小美想知道,s和t的最大字符串匹配度是多少?
分析
-
定义一个result变量来记录匹配度,初始化result为0
-
首先遍历字符串s和字符串t的前n位,
- 如果
s[i]==t[i],result++
- 否则,将
s[i]!=t[i]
的值进行一个映射,也就是pair<char,char>(s[i],t[i])
,将其加入strVec中;并且将s[i]和t[i]分别加入s1Vec和s2Vec中。
- 如果
-
匹配度有三种情况
- 匹配度等于result
- 匹配度等于result+2,此时交换t中的两个字符均会与s中对应的字符匹配
- 匹配度等于result+1,此时交换t中的两个字符只有一个会与s中对应的字符匹配
-
(1)如果
pair<char,char>(t[i],s[i])
在strVec中存在,此时在字符串t中交换这两个字符,匹配度就为result+2; -
(2)如果(1)中的条件不满足,就判断s2Vec中是否存在s1Vec的字符,如果存在,匹配度就为result+1;
-
(3)如果(1)和(2)的条件都不满足,就输出result;
代码
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
int nums2(string s1, string s2, int n) {
int result = 0;
vector<pair<char, char>> strVec;
vector<char> s1Vec;
vector<char> s2Vec;
for (int i = 0; i < n; i++) {
if (s1[i] == s2[i]) {
result++;
}
else {
strVec.emplace_back(pair<char, char>(s1[i], s2[i]));
s1Vec.emplace_back(s1[i]);
s2Vec.emplace_back(s2[i]);
}
}
for (auto& m : strVec) {
if (find(strVec.begin(), strVec.end(), pair<char, char>(m.second, m.first)) != strVec.end()) {
return result + 2;
}
}
for (auto& m : s1Vec) {
if (find(s2Vec.begin(), s2Vec.end(), m) != s2Vec.end()) {
return result + 1;
}
}
return result;
}
int main() {
int n;
cin >> n;
cin.ignore();
string s1;
cin >> s1;
cin.ignore();
string s2;
cin >> s2;
cout << nums2(s1, s2, n) << endl;
system("pause");
return 0;
}
3.小美的树上染色
题目描述
小美拿到了一棵树,每个节点有一个权值。初始每个节点都是白色。
小美有若干次操作,每次操作可以选择两个相邻的节点,如果它们都是白色且权值的乘积是完全平方数,小美就可以把这两个节点同时染红。
小美想知道,自己最多可以染红多少个节点?
分析
这道题目可以采用贪心或者树形DP。
-
贪心的解法是从叶子节点往上寻找,找到两个满足要求的节点,就将其染色,但是为什么从叶子节点开始开始染色,最终的染色节点数就是最多的?
-
树形DP:
以下图片来自树上染色 ,这里关于dp[i][1]还是很不容易想的
-
dp[i][0] += max(dp[son][0], dp[son][1]);
当前节点不染色的最大染色节点数 = 当前节点不染色的最大染色节点数 + 当前节点子节点不染色的最大节点数 和 当前节点子节点染色的最大节点数的最大值
-
dp[i][1] = max(dp[i][1],(dp[i][0] - max(dp[son][0], dp[son][1]) + dp[son][0] + 2));
dp[i][1] = 当前节点的子节点j不染色的最大值 + 2 + 除j节点以外的其他子节点max(染色的最大值,不染色的最大值)。
dp[i][1]表示这个结点要找一个子节点一起染色,其他子节点不染色,dp[i][0]就是所有子节点不染色的最大值之和,减去当前结点的,再加上dp[v][0]+2就是这种情况的值(还是不太懂,希望评论区有大佬能够指点以下)
代码
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include<cmath>
using namespace std;
typedef long long LL;
int st[100000000];
bool fun(LL x)
{
LL d = sqrt(x);
return d * d == x;
}
void dfs1(int u, int fa, vector<vector<int>>& graph, vector<int>& weight, int& ans) {
for (auto& x : graph[u]) {
if (x == fa) continue;
dfs1(x, u, graph, weight, ans);
if (fun(weight[u] * weight[x]) && (!st[x] && !st[u])) {
ans += 2;
st[x] = 1;
st[u] = 1;
}
}
}
//树形dp会超时
void dfs2(vector<vector<int>>& graph, vector<int>& weight, vector<vector<int>>& dp, int u, int fa) {
for (int v : graph[u]) {
if (v == fa) continue;
dfs2(graph, weight, dp, v, u);
dp[u][0] += max(dp[v][0], dp[v][1]);
}
for (int v : graph[u]) {
if (v == fa) continue;
dfs2(graph, weight, dp, v, u);
if (fun(weight[u] * weight[v])) {
dp[u][1] = max(dp[u][1],
(dp[u][0] - max(dp[v][0], dp[v][1]) + dp[v][0] + 2));
}
}
}
int main() {
//节点数量
int n;
cin >> n;
cin.ignore();
//每个节点的权值
vector<int> weight(n);
string line;
getline(cin, line);
istringstream iss(line);
for (int i = 0; i < n; i++) {
string s;
iss >> s;
int w = stoi(s);
weight[i] = w;
}
//邻接表
vector<vector<int>> graph(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
graph[v - 1].push_back(u - 1);
graph[u - 1].push_back(v - 1);
}
int ans = 0;
//dfs(0, -1, graph, weight, ans);
//cout << ans << endl;
//定义一个二维dp数组
//dp[i][0]表示第i个节点不染色的最大染色节点数
//dp[i][1]表示第i个节点染色的最大染色节点数
vector<vector<int>> dp(n, vector<int>(2, 0));
dfs2(graph, weight, dp, 0, -1);
cout << max(dp[0][0], dp[0][1]);
return 0;
}
4.小美的排列询问
题目描述
小美拿到了一个排列。她想知道在这个排列中,x和y是否是相邻的。你能帮帮她吗?
排列是指一个长度为n的数组,其中 1 到n每个元素恰好出现一次。
分析
-
因为要判断x和y是否相邻,因此我们将数组中所有相邻的元素都以成对的形式重新进行组装
-
vector<pair<string, string>> strPair; for (int i = 1; i < n; i++) { strPair.emplace_back(pair<string, string>(strChar[i], strChar[i - 1])); }
-
例如,如果strChar={1,4,2,3},那么上述代码输出的strPair={{1,4}, {4,2}, {2,3}}
-
最后判断{x,y}或者{y,x}是否在strPair中出现即可
-
代码
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
cin.ignore();
string line;
getline(cin, line);
istringstream iss(line);
vector<string> strChar(n);
for (int i = 0; i < n; i++) {
iss >> strChar[i];
}
string x;
string y;
cin >> x >> y;
pair<string, string> xy(x, y);
pair<string, string> yx(y, x);
//将数组中所有相邻的元素都以成对的形式重新进行组装
vector<pair<string, string>> strPair;
for (int i = 1; i < n; i++) {
strPair.emplace_back(pair<string, string>(strChar[i], strChar[i - 1]));
}
//判断{x,y}或者{y,x}是否在strPair中出现
if (find(strPair.begin(), strPair.end(), xy) != strPair.end() || find(strPair.begin(), strPair.end(), yx) != strPair.end()) {
cout << "Yes" << endl;
return 0;
}
cout << "No" << endl;
system("pause");
return 0;
}
5.小美的排列构造
题目描述
小美定义一个数组𝑎a的权值计算如下:
首先将a的每一对相邻两项求和,得到一个b数组。那么𝑏b数组的最大值减最小值即为a数组的权值。
例如,若𝑎=[2,1,3],那么𝑏=[3,4],b数组的极差是1。因此a数组的权值为1。
现在小美希望你能构造一个长度为n的排列,满足权值尽可能小。你能帮帮她吗?
排列是指一个长度为n的数组,其中 1 到n每个元素恰好出现一次。
分析
- 为了减弱最大值和最小值的影响,因此在构建数组的时候,给一个最大值、一个最小值、一个次最大值、一个次最小值…以此往下进行构建数组,至于为什么这样构造出来的数组就符合要求,我也想不太明白。
- 本题采用一个双指针法
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void nums5(int n) {
vector<int> a;
vector<int> b;
//left指向最小值,right指向最大值
int left = 1;
int right = n;
while (left <= right) {
//添加一个最大值,再添加一个最小值
a.emplace_back(right);
a.emplace_back(left);
left++;
right--;
}
for (int i = 0; i < n; i++) {
if (i != n - 1) cout << a[i] << " ";
else cout << a[i];
}
}
int main() {
int n;
cin >> n;
nums5(n);
system("pause");
return 0;
}