【美团2021校招编程题】
1、淘汰分数
题目描述:
某比赛已经进入了淘汰赛阶段,已知共有n名选手参与了此阶段比赛,他们的得分分别是a_1,a_2….a_n,小美作为比赛的裁判希望设定一个分数线m,使得所有分数大于m的选手晋级,其他人淘汰。
但是为了保护粉丝脆弱的心脏,小美希望晋级和淘汰的人数均在[x,y]之间。
显然这个m有可能是不存在的,也有可能存在多个m,如果不存在,请你输出-1,如果存在多个,请你输出符合条件的最低的分数线。
输入:
输入第一行仅包含三个正整数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
思路分析:
- 将成绩从低到高依次压入栈中,栈中存放淘汰者的分数
- 栈的长度则为淘汰者人数,使其处于[x, y]之间,晋级人数为n - stack.size()也处于[x, y]之间
- 第一个满足条件的成绩则为最低分数线,将其弹出
代码实现:
import java.io.*;
import java.util.*;
/**
* @author Jeremy Li
* @data 2021/2/7 - 12:22
*/
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String[] condition = br.readLine().trim().split(" ");
int n = Integer.parseInt(condition[0]);
int x = Integer.parseInt(condition[1]);
int y = Integer.parseInt(condition[2]);
int[] score = new int[n];
String[] scores = br.readLine().trim().split(" ");
for (int i = 0; i < n; i++) {
score[i] = Integer.parseInt(scores[i]);
}
bw.write(Integer.toString(solution(n, x, y, score)));
br.close();
bw.close();
}
public static int solution(int n, int x, int y, int[] score) {
// 将成绩进行排序
Arrays.sort(score);
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < n; i++) {
stack.push(score[i]);
// 让淘汰和晋级的人数都在[x,y]区间之内
if ((stack.size() >= x && stack.size() <= y) && (n - stack.size() >= x && n - stack.size() <= y)){
break;
}
}
// 根据stack的size判断是跳出循环还是循环结束
if (stack.size() != n) return stack.pop();
return -1;
}
}
2、正则序列
题目描述:
我们称一个长度为n的序列为正则序列,当且仅当该序列是一个由1~n组成的排列,即该序列由n个正整数组成,取值在[1,n]范围,且不存在重复的数,同时正则序列不要求排序
有一天小团得到了一个长度为n的任意序列,他需要在有限次操作内,将这个序列变成一个正则序列,每次操作他可以任选序列中的一个数字,并将该数字加一或者减一。
请问他最少用多少次操作可以把这个序列变成正则序列?
输入描述:
输入第一行仅包含一个正整数n,表示任意序列的长度。(1<=n<=20000)
输入第二行包含n个整数,表示给出的序列,每个数的绝对值都小于10000。
输出描述:
输出仅包含一个整数,表示最少的操作数量。
输入例子1:
5
-1 2 3 10 100
输出例子1:
103
思路分析:
1、要想实现最少的操作次数,则将每个位置的数字通过操作得到正则序列中该位置所对应数字,此时操作次数最少
代码实现:
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int n = Integer.parseInt(br.readLine().trim());
String[] arr = br.readLine().trim().split(" ");
int[] res = new int[n];
for (int i = 0; i < n; i++) {
res[i] = Integer.parseInt(arr[i]);
}
int count = getCount(n, res);
bw.write(String.valueOf(count));
br.close();
bw.close();
}
public static int getCount(int n, int[] res) {
Arrays.sort(res);
int count = 0;
// 这里从1开始,因为正则序列也是从1开始的,i可以表示正则序列中该位置的值
for (int i = 1; i <= n; i++) {
// 输入数组中同位置的数与正则序列中对应的数相减取绝对值,即为进行操作的次数
count += Math.abs(res[i - 1] - i);
}
return count;
}
}
3、公司食堂
题目描述:
小美和小团所在公司的食堂有N张餐桌,从左到右摆成一排,每张餐桌有2张餐椅供至多2人用餐,公司职员排队进入食堂用餐。小美发现职员用餐的一个规律并告诉小团:当男职员进入食堂时,他会优先选择已经坐有1人的餐桌用餐,只有当每张餐桌要么空着要么坐满2人时,他才会考虑空着的餐桌;
当女职员进入食堂时,她会优先选择未坐人的餐桌用餐,只有当每张餐桌都坐有至少1人时,她才会考虑已经坐有1人的餐桌;
无论男女,当有多张餐桌供职员选择时,他会选择最靠左的餐桌用餐。现在食堂内已有若干人在用餐,另外M个人正排队进入食堂,小团会根据小美告诉他的规律预测排队的每个人分别会坐哪张餐桌。
输入描述:
第一行输入一个整数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:
1
5
01102
6
MFMMFF
输出例子1:
2
1
1
3
4
4
思路分析:
1、桌子有三种状态:空桌、有一人、有两人(满),每种状态有一定数量的桌子,则可以考虑用list或者stack存放这三种状态的桌子,每个位置可以存放一个队列。这里为什么选择队列来存放桌子索引?因为题中规定无论男女,当有多张餐桌供职员选择时,他会选择最靠左的餐桌用餐,则利用队列的FIFO特性存放桌子索引
2、依次判断进入员工的性别,若为男性,则首先判断单人桌是否有空余,即list或stack中索引为1的位置存放的队列是否为空,若为空,则从左起找空桌(将容器中索引为0存放的队列的元素弹出,并加入索引为1的队列中),若不为空,则选择该桌,并加入索引为2的队列中。女性同理。
3、将每个员工的选择存入数组中,返回数组。
代码实现:
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
// 数组组号
int T = Integer.parseInt(br.readLine());
for (int i = 0; i < T; i++) {
// 桌子的个数
int N = Integer.parseInt(br.readLine());
// 每张桌子的状态
String tables = br.readLine();
// 此时剩余座位可容纳的人数
int M = Integer.parseInt(br.readLine());
// 进入餐厅的员工性别
String enters = br.readLine();
int[] res = solution(tables, enters);
for (int r : res) {
bw.write(Integer.toString(r));
bw.newLine();
}
}
br.close();
bw.close();
}
public static int[] solution(String tables, String enters) {
Stack<PriorityQueue<Integer>> stack = new Stack<>();
// 初始化stack
stack.add(new PriorityQueue<>());
stack.add(new PriorityQueue<>());
stack.add(new PriorityQueue<>());
// 从左到右依次将每个桌子的索引存到对应状态的位置
for (int i = 0; i < tables.length(); i++) {
stack.get(tables.charAt(i) - '0').add(i + 1);
}
int[] res = new int[enters.length()];
// 员工开始进入
for (int i = 0; i < enters.length(); i++) {
// 每个员工的选择
int choose;
if (enters.charAt(i) == 'M'){
// 当没有单人桌时
if (stack.get(1).isEmpty()){
choose = stack.get(0).poll();
stack.get(1).add(choose);
}else {
// 当有单人桌时
choose = stack.get(1).poll();
stack.get(2).add(choose);
}
}else {
if (stack.get(0).isEmpty()){
choose = stack.get(1).poll();
stack.get(2).add(choose);
}else {
choose = stack.get(0).poll();
stack.get(1).add(choose);
}
}
res[i] = choose;
}
// 这里我最初是直接用system.out.println进行打印,运行结果超时
return res;
}