Acwing第56场周赛
第一题:4482. 分组
题目:给定一个长度为n的数组 a1,a2,…,an。请你将这n个元素重新分组,要求每个组内的元素两两不等,且分组数量应尽可能少。请你计算最少所需的分组数量。
分析:要保证每个分组里面没有重复的元素,就要将重复的元素分别放在一个新的组,所以分组的数目即为重复最多的元素重复的次数。我们可以构建一个数组或者哈希表,统计每个元素出现的次数,然后取最大值即可。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 110;
int n;
int cnt[N];//用于存储元素出现的次数
int main()
{
cin >> n;
int res = 0;
for(int i = 0;i < n; i ++){
int x;
cin >> x;
cnt[x] ++;//x出现的次数加1
res = max(res,cnt[x]);
}
cout << res << endl;
return 0;
}
第二题:4483.格斗场
题目:一个格斗场内有n个战士,其中第i个战士的战斗力为ai。作为格斗场内的经理人,你需要给战士们安排一对一的决斗。这些决斗是一场接一场进行的,一场结束后才会安排下一场。为了保证决斗的观赏性,在安排时需保证:
- 决斗双方的战斗力不能相同。
- 决斗双方的战斗力差距不能超过K。
已知,在决斗中战斗力高的选手一定可以将战斗力低的选手击败,并且失败的选手会被赶出格斗场。请你合理安排决斗,使得当剩余选手之间无法再安排任何决斗时,剩余选手的数量越少越好。请你输出剩余选手的最小可能数量。
分析:将n个战士的战斗力从低到高排序,必定是战斗力高的战士来淘汰战斗力低的战士,当且仅当两者战斗力之差大于0小于k时战斗力低的被淘汰。只需在这个有序战斗力数组中找到一个aj使得aj为满足aj-ai>k的最小数,则a(j-1)就是满足条件的最大值。每找到一个满足条件的a(j-1),淘汰人数就增加一个,即存活人数就减一。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200010;
int n, k;
int a[N];
int main()
{
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
sort(a, a + n);//将战斗力从小到大排序(sort函数无说明时默认从小到大)
int res = n;//留下的战士数为总人数减去被淘汰的
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < n && a[j] - a[i] <= k) j ++ ;//从前往后找到一个j使得a[j]-a[i] > k就停止循环
if (a[j - 1] > a[i]) res -- ;//检验此时a[j-1]满足大于a[i]表示a[i]可以被淘汰,存活士兵数减1,接下来进入下一个循环检测战斗力a[i+1]的士兵是否会被淘汰
}
printf("%d\n", res);
return 0;
}
力扣第298场周赛
第一题:5242.兼具大小写的最好英文字母
题目:给你一个由英文字母组成的字符串 s
,请你找出并返回 s
中的 最好 英文字母。返回的字母必须为大写形式。如果不存在满足条件的字母,则返回一个空字符串。最好 英文字母的大写和小写形式必须 都 在 s
中出现。英文字母 b
比另一个英文字母 a
更好 的前提是:英文字母表中字母b
在字母 a
之 后 出现。
分析:分别用两个数组存储字符串s中的字母出现大写字母和小写字母的情况,存在则在数组中数值加一,不存在则为0,当且仅当该字母的两个数组都不为0时表示大小写均存在,再转化为大写字母输出。
class Solution {
public:
string greatestLetter(string s) {
int m[26];//存放大写字母存在情况
int n[26];//存放小写字母存在情况
memset(m, 0, sizeof m);//memset函数把数组m中所有数字换做0,对数组初始化。
memset(n, 0, sizeof n);
for(int i = 0; i < s.size(); i ++)
{
if(s[i] >= 'A' && s[i] <= 'Z')m[s[i] - 'A'] ++;//如果该字母在大写字母范围内则对应的大写字母标记位加1(因为可能不止出现一次相同大写字母)
else n[s[i] - 'a'] ++;
}
string res;
for(int i = 0; i < 26; i ++)
{
if(m[i] && n[i])res = i + 'A';//当且仅当m[i]和n[i]都为true(0为false)时,将i转化为大写字母并输出
}
return res;
}
};
第二题:2310.个位数字为k的整数之和
题目:给你两个整数 num 和 k ,考虑具有以下属性的正整数多重集:每个整数个位数字都是 k 。
所有整数之和是 num 。返回该多重集的最小大小,如果不存在这样的多重集,返回 -1 。注意:多重集与集合类似,但多重集可以包含多个同一整数,空多重集的和为 0 。个位数字是数字最右边的数位。
分析:多重集中每个满足条件的数可以表示成 10 的倍数加上 k 的形式。由于这 n 个数都以 k 结尾,可得num - nk必须是10的倍数。从小到大枚举得到的最小的n即是我们要找的答案,找到第一个满足 num-nk 是 10 的倍数的 n。而n最大不会超过num。
代码:
class Solution {
public int minimumNumbers(int num, int k) {
if(num == 0)return 0;//n最小为1,最大不超过num
for(int n = 1;n <= num && num - n*k >= 0;n ++){
if((num - n*k)%10 == 0){
return n;
}
}
return -1;//未找到
}
};
其中n的上限还可以进行优化:因为num%10=(n*k)%10,而(n*k)%10又可转化为(n%10)*(k%10),当n=11时和n=1时的结果重复,所以n最大可以取到10。即for循环中n<=num可优化为n<=10