刷题记录04-美团2021 通用编程题第10场

1.淘汰分数

某比赛已经进入了淘汰赛阶段,已知共有n名选手参与了此阶段比赛,他们的得分分别是a_1,a_2….a_n,小美作为比赛的裁判希望设定一个分数线m,使得所有分数大于m的选手晋级,其他人淘汰。但是为了保护粉丝脆弱的心脏,小美希望晋级和淘汰的人数均在[x,y]之间。显然这个m有可能是不存在的,也有可能存在多个m,如果不存在,请你输出-1,如果存在多个,请你输出符合条件的最低的分数线。
数据范围:1≤n≤50000 ,1≤x≤y≤n
进阶:时间复杂度O(nlogn) ,空间复杂度O(n)

输入描述

输入第一行仅包含三个正整数n,x,y,分别表示参赛的人数和晋级淘汰人数区间。(1<=n<=50000,1<=x,y<=n)
输入第二行包含n个整数,中间用空格隔开,表示从1号选手到n号选手的成绩。(1<=|a_i|<=1000)

输出描述

输出仅包含一个整数,如果不存在这样的m,则输出-1,否则输出符合条件的最小的值。

示例

输入例子:
6 2 3
1 2 3 4 5 6
输出例子:
3

解题思路

题目很简单,但是看懂题目意思不是很简单,就是说要一个值属于给定范围,将数组分成两段并且两段的值都属于给定范围,不然就返回-1。1A秒了。

代码

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main() {
	int N, mini, maxi;
	cin >> N >> mini >> maxi;
	vector<int> score(N);
	for (int i = 0; i < N; ++i) {
		cin >> score[i];
	}
	sort(score.begin(), score.end());
	int i = mini, j = maxi;
	while (i <= j) {
		if (j >= N - i)
			break;
		else
			i++;
	}
	if (i + j >= N) {
		cout << score[i - 1] << endl;
	}
	else {
		cout << -1 << endl;
	}
}

2.正则序列

我们称一个长度为n的序列为正则序列,当且仅当该序列是一个由1~n组成的排列,即该序列由n个正整数组成,取值在[1,n]范围,且不存在重复的数,同时正则序列不要求排序。有一天小团得到了一个长度为n的任意序列s,他需要在有限次操作内,将这个序列变成一个正则序列,每次操作他可以任选序列中的一个数字,并将该数字加一或者减一。
请问他最少用多少次操作可以把这个序列变成正则序列?
数据范围:
1≤n≤20000 ,0≤abs(s^i)≤10000 进阶:时间复杂度O(n) ,空间复杂度O(n)

输入描述

输入第一行仅包含一个正整数n,表示任意序列的长度。(1<=n<=20000)
输入第二行包含n个整数,表示给出的序列,每个数的绝对值都小于10000。

输出描述

输出仅包含一个整数,表示最少的操作数量。

示例

输入例子:
5
-1 2 3 10 100
输出例子:
103

解题思路

题目意思就是说,给你一个N, 再给你一串数,让你把这串数变成 1 2 3 …N 要操作多少次,排个序,1A秒了。

代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
	int N;
	cin >> N;
	vector<int> nums(N);
	for (int i = 0; i < N; ++i) {
		cin >> nums[i];
	}
	sort(nums.begin(), nums.end());
	int ret = 0;
	for (int i = 1; i <= N; ++i) {
		ret += abs(i - nums[i - 1]);
	}
	cout << ret << endl;
	return 0;
}

3.公司食堂

小美和小团所在公司的食堂有N张餐桌,从左到右摆成一排,每张餐桌有2张餐椅供至多2人用餐,公司职员排队进入食堂用餐。小美发现职员用餐的一个规律并告诉小团:当男职员进入食堂时,他会优先选择已经坐有1人的餐桌用餐,只有当每张餐桌要么空着要么坐满2人时,他才会考虑空着的餐桌;
当女职员进入食堂时,她会优先选择未坐人的餐桌用餐,只有当每张餐桌都坐有至少1人时,她才会考虑已经坐有1人的餐桌;
无论男女,当有多张餐桌供职员选择时,他会选择最靠左的餐桌用餐。现在食堂内已有若干人在用餐,另外M个人正排队进入食堂,小团会根据小美告诉他的规律预测排队的每个人分别会坐哪张餐桌。
进阶:时间复杂度O(nlogn) ,空间复杂度O(n)

输入描述

第一行输入一个整数T(1<=T<=10),表示数据组数。
每组数据占四行,第一行输入一个整数N(1<=N<=500000);
第二行输入一个长度为N且仅包含数字0、1、2的字符串,第i个数字表示左起第i张餐桌已坐有的用餐人数;
第三行输入一个整数M(1<=M<=2N且保证排队的每个人进入食堂时都有可供选择的餐桌);
第四行输入一个长度为M且仅包含字母M、F的字符串,若第i个字母为M,则排在第i的人为男性,否则其为女性。

输出描述

每组数据输出占M行,第i行输出一个整数j(1<=j<=N),表示排在第i的人将选择左起第j张餐桌用餐。

示例

输入例子:
1
5
01102
6
MFMMFF
输出例子:
2
1
1
3
4
4

解题思路

这题算是模拟类型的题目吧,用个二维数组作为桌子模拟判断。

代码

#include <iostream>
#include <vector>
using namespace std;

int main() {
	int g;
	cin >> g;
	while(g-->0) {
		int N;              // 多少张桌子
		cin >> N;
		vector<vector<bool>> table(N, vector<bool>(2, false));       // 模拟餐桌 有没有人坐 true 有人坐, false 没人坐
		string input;
		getline(cin.ignore(), input);
		for (int i = 0; i < input.size(); ++i) {
			if (input[i] == '0') {
				continue;
			}
			else if (input[i] == '1') {
				table[i][0] = true;
			}
			else {
				table[i][0] = table[i][1] = true;
			}
		}
		int P;              // 来多少人
		cin >> P;
		string people;
		getline(cin.ignore(), people);
		for (int i = 0; i < P; ++i) {
			if (people[i] == 'M') {
				int ret = -1;
				for (int j = 0; j < N; ++j) {
					if (table[j][0] == true && table[j][1] == false) {
						table[j][1] = true;
						ret = j;
						break;
					}
				}
				if (ret == -1) {
					for (int j = 0; j < N; ++j) {
						if (table[j][0] == false) {
							table[j][0] = true;
							ret = j;
							break;
						}
					}
				}
				cout << ret + 1 << endl;
			}
			else {
				int ret = -1;
				for (int j = 0; j < N; ++j) {
					if (table[j][0] == false) {
						table[j][0] = true;
						ret = j;
						break;
					}
				}
				if (ret == -1) {
					for (int j = 0; j < N; ++j) {
						if (table[j][1] == false) {
							table[j][1] = true;
							ret = j;
							break;
						}
					}
				}
				cout << ret + 1 << endl;
			}
		}
	}
	return 0;
}

这题在第八组(200000个人的时候)超时了,看了评论说把 endl 换成"\n"就不超时了,但是我还是超时,可能是要用队列来做吧。

4.最优二叉树II

小团有一个由N个节点组成的二叉树,每个节点有一个权值。定义二叉树每条边的开销为其两端节点权值的乘积,二叉树的总开销即每条边的开销之和。小团按照二叉树的中序遍历依次记录下每个节点的权值,即他记录下了N个数,第i个数表示位于中序遍历第i个位置的节点的权值。之后由于某种原因,小团遗忘了二叉树的具体结构。在所有可能的二叉树中,总开销最小的二叉树被称为最优二叉树。现在,小团请小美求出最优二叉树的总开销。

输入描述

第一行输入一个整数N(1<=N<=300),表示二叉树的节点数。
第二行输入N个由空格隔开的整数,表示按中序遍历记录下的各个节点的权值,所有权值均为不超过1000的正整数。

输出描述

输出一个整数,表示最优二叉树的总开销。

示例

输入例子:
5
7 6 5 1 3
输出例子:
45
例子说明:
题目

最优二叉树如图所示,总开销为71+65+51+13=45。

解题思路

这个题目需要用树型动态规划来做,dp就是遍历任何一个作为根结点,比较min(当前结点作为根结点的,当前结点*父结点 + 深度遍历左边 + 深度遍历右边)。动态规划就体现在,计算过的以p为根结点,l为左,r为右的值不用再计算,用空间换时间给存储下来。

代码

#include <iostream>
#include <vector>
#include <limits.h>
using namespace std;

// 备忘录,记录已经计算过的  ( 以 p 为根, l 是 p 的左结点 r 是 p 的右结点, l==r 就是只有一个结点) 的最小值
vector<vector<vector<int>>> mem;
int dfs(vector<int>& nodes, int l, int r, int p) {  // l r 区间的树,根结点是 p
	if (l > r) return 0;
	if (mem[l][r][p] != -1) return mem[l][r][p];
	int ret = INT_MAX;
	for (int i = l; i <= r; ++i) {
		ret = min(ret, nodes[i] * nodes[p] + dfs(nodes, l, i - 1, i) + dfs(nodes, i + 1, r, i));
	}
	return mem[l][r][p] = ret;
}

int main() {
	int rootnum;
	cin >> rootnum;
	vector<int> nodes(rootnum+1);
	for (int i = 1; i <= rootnum; ++i) {
		cin >> nodes[i];
	}

	vector<int> tmp1(rootnum+1, -1);
	vector<vector<int>> tmp2(rootnum+1, tmp1);
	mem.resize(rootnum+1, tmp2);

	cout << dfs(nodes, 1, rootnum, 0) << endl;

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值