目录
第一题:乒乓球筐
题目链接:乒乓球筐__牛客网
nowcoder有两盒(A、B)乒乓球,有红双喜的、有亚力亚的……现在他需要判别A盒是否包含了B盒中所有的种类,并且每种球的数量不少于B盒中的数量,该怎么办呢?
输入描述:
输入有多组数据。
每组数据包含两个字符串A、B,代表A盒与B盒中的乒乓球,每个乒乓球用一个大写字母表示,即相同类型的乒乓球为相同的大写字母。
字符串长度不大于10000。
输出描述:
每一组输入对应一行输出:如果B盒中所有球的类型在A中都有,并且每种球的数量都不大于A,则输出“Yes”;否则输出“No”。
示例1
输入
ABCDFYE CDE
ABCDGEAS CDECDE
输出
Yes
No
题意概括起来就是判断B是否是A的子集,如果是,输出Yes;否则输出No。
解法一:用两个hash数组来统计两个字符串中每个字符出现的次数,然后遍历字符串B,只要B中所有出现的字符的个数都小于等于A中对应字符的次数,B就是A的子集。反之,则不是。
解法二:用一个hash数组来统计A中各个字符出现的次数,然后遍历B,通过B中字符找到该字符在hash数组中的位置,然后减一即可。如果hash数组中有某个位置被减到小于0,那么说明B中某个字符出现的次数大于A中出现的次数或者是B中有的字符A中没有,输出No;反之输出Yes。
这两种解法都可以解决问题,但是为了减少一个空间上的开销,下面我采用解法二。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string a, b;
while(cin >> a >> b)
{
int hash[26] = { 0 };
for(auto ch : a) hash[ch - 'A']++;
bool flag = true;
for(auto ch : b)
{
if(--hash[ch - 'A'] < 0)
{
flag = false;
break;
}
}
if(flag) cout << "Yes" << endl;
else cout << "No" << endl;
}
}
第二题:组队竞赛
题目链接:组队竞赛_牛客笔试题_牛客网
牛牛举办了一次编程比赛,参加比赛的有3*n个选手,每个选手都有一个水平值a_i.现在要将这些选手进行组队,一共组成n个队伍,即每个队伍3人.牛牛发现队伍的水平值等于该队伍队员中第二高水平值。
例如:
一个队伍三个队员的水平值分别是3,3,3.那么队伍的水平值是3
一个队伍三个队员的水平值分别是3,2,3.那么队伍的水平值是3
一个队伍三个队员的水平值分别是1,5,2.那么队伍的水平值是2
为了让比赛更有看点,牛牛想安排队伍使所有队伍的水平值总和最大。
如样例所示:
如果牛牛把6个队员划分到两个队伍
如果方案为:
team1:{1,2,5}, team2:{5,5,8}, 这时候水平值总和为7.
而如果方案为:
team1:{2,5,8}, team2:{1,5,5}, 这时候水平值总和为10.
没有比总和为10更大的方案,所以输出10.
输入描述:
输入的第一行为一个正整数n(1 ≤ n ≤ 10^5)
第二行包括3*n个整数a_i(1 ≤ a_i ≤ 10^9),表示每个参赛选手的水平值.
输出描述:
输出一个整数表示所有队伍的水平值总和最大值.
示例1
输入
2
5 2 8 5 1 5
输出
10
排序后,我们很想其中一组的水平值为8,但是根本做不到。只能退而求其次,只能取第二大的数,要取第二大的数5,就得顺带把最大的8放到同一组,不然它又是最大的。
此时第一组还要在填入一个数, 我们不可能把5填进去,因为会白白浪费掉一个大的数,导致总和不是最大的。所以我们会把1放进去。第一组就为 1 5 8.水平值为5.
同理,第二组也是同样的策略。把剩余的最大的数和次大的数放到第二组,然后在把剩下的数中最小的也加入第二组,进而得到第二组为 2 5 5.水平值为5.
圈起来的数就是我们取到的最大的水平值。规律就是:排序后,从倒数第二个数开始取,取完倒数第二个数后,往前隔一个位置再取,直到取出n个。把取出的这些数加起来就是答案。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int arr[N *3];
int main()
{
int n;
cin >> n;
for(int i = 0; i < 3 * n; i++) cin >> arr[i];
sort(arr, arr + 3 * n);//排序
long long ret = 0;//int会溢出
int pos = 3 * n - 2;//倒数第二个位置
while(n--)
{
ret += arr[pos];
pos -= 2;//隔一个在取
}
cout << ret << endl;
return 0;
}
第三题:删除相邻数字的最大分数
题目链接: 删除相邻数字的最大分数_牛客题霸_牛客网
描述
给定一个长度为 n 的仅包含正整数的数组,另外有一些操作,每次操作你可以选择数组中的任意一个元素 𝑎𝑖 ,同时数组中所有等于 𝑎𝑖−1 和 𝑎𝑖+1 的元素会被全部移除,同时你可以得到 𝑎𝑖 分,直到所有的元素都被选择或者删除。
请你计算最多能得到多少分。
数据范围: 数组长度满足 1≤𝑛≤10^5 1≤n≤10^5 ,数组中的元素大小都满足 1≤𝑎𝑖≤10^4.
输入描述:
第一行输入一个正整数 n 表示数组的长度
第二行输入 n 个数字表示数组的各个元素值。
输出描述:
输出能得到的最大分数。
示例1
输入:
9
1 2 1 3 2 2 2 2 3
输出:
10
说明:第一步选择一个 2 ,然后所有 1 和 3 都被移除了,此时数组中剩下的是 [2,2,2,2] ,依次选择他们即可得到 10 分
通过对数组进行预处理,然后转化为另个一问题。
预处理用做的就是:统计数组中每个数字的总和,存放进一个hash数组中,由题意,选中一个数以后,该数+1和-1的数就不能再选,在hash数组中的表现就是,选中一个数后,它的相邻位置就不能再选了(类似于打家劫舍问题,不知道也没关系)。所以可以用动态规划来解决这个问题。
动态规划五部曲:
1)状态表示:
对于hash数组中的某一个位置,可选可不选,因此状态表示有两种。
f [ i ]表示选该位置得到的最大分数。g[ i ]表示不选该位置的得到的最大分数。
2)推导状态转移方程:
f[ i ] = hash[ i ] + g[ i - 1] g[ i ] = max(f [ i - 1],g[ i - 1])
如果选择 i 位置的值,那么前面i - 1位置就不能选,就是f [ i ] 的最大值就是i对应的和+不选i - 1位置的最大分数,而不选 i - 1的最大分数就是存在g[i - 1]中,故得到f[i]的状态表示。
如果不选 i 位置,那么前面的 i - 1位置有两种情况,可以选 i - 1位置,也可以不选 i - 1位置,但是g[ i ]表示不选 i 位置的最大分数,所以g[ i ]就取选和不选这两种情况种的最大值。
3)初始化
从1开始填,因为都是正整数,没有0,所以f[0]和g[0]都为0。
4)填表顺序
因为后面的状态会依赖于前面的,所以从左往右填。
5)确定返回值
返回值就是max(f[N - 1],g[N - 1])。这样可以确保所有数都被选择或者被删除过了。
#include <iostream>
using namespace std;
const int N = 1e4 + 10;
int sum[N] = { 0 };//hash数组
int f[N], g[N]; //dp数组
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
int x;
cin >> x;
sum[x] += x;//统计数字的总和
}
for(int i = 1; i < N; i++)
{
//填表
f[i] = sum[i] + g[i - 1];
g[i] = max(f[i - 1], g[i - 1]);
}
cout << max(f[N - 1], g[N - 1]) << endl;
return 0;
}