Bootstrap

【牛客】美团2024秋招笔试第一场编程真题(1-5)

美团2024届秋招笔试第一场编程真题

题目链接美团2024届秋招笔试第一场编程真题

1.小美的外卖订单

题目描述

小美正在设计美团外卖的定价信息。已知外卖定价的规则如下:

  1. 每道菜有折扣价和原价。折扣价不能超过原价。
  2. 订单有满𝑥x元减𝑦y元的优惠。当购买的菜的价格总和不小于𝑥x元时,总价格可以减𝑦y元。“减”的价格不能超过“满”的价格。
  3. 满减优惠和折扣价是互斥的,当且仅当每个菜都选择了原价才可以触发满减。
  4. 系统会自动为客户计算最低价格的方案。

在设计定价时,原价、折扣价和满减的价格都必须是正实数。如果设计的定价发生问题,则会提示数据错误。
请使用等价划分法设计测试用例,来测试该系统的功能。

分析

  • 求出所有物品的原价和折扣价
  • 如果原价满足满减要求,求出满减在之后的价格
  • 输出折扣价和满减之后的价格的最小值

代码

#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只包含小写字母的字符串st,小美定义“两个字符串的匹配度”为𝑖∈[1,𝑛]中𝑠[𝑖]=𝑡[𝑖]的数量,例如"abacd"和"aabdd"的匹配度就是2。

现在你可以进行最多一次以下操作:
对于字符串t,选择两个索引𝑖,𝑗(1≤𝑖<𝑗≤𝑛)i,j(1≤i<jn),交换𝑡[𝑖]​和𝑡[𝑗]​。

小美想知道,st的最大字符串匹配度是多少?

分析

  • 定义一个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.小美的排列询问

题目描述

小美拿到了一个排列。她想知道在这个排列中,xy是否是相邻的。你能帮帮她吗?

排列是指一个长度为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;
}
;