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;
}