本文为AtCoder Beginner Contest 393 A - F详细题解,觉得有帮助或者写的不错可以点个赞,没有打这个比赛的, 也可以收藏起来之后vp看(
目录
题目A:
题目大意:
有四种食物,分别是1, 2, 3, 4,其中有一种食物会让人产生不适
第一个人吃了1和2的食物,第二个人吃了1和3的食物
现在知道这两个人的状态(健康或者生病),请问哪一种食物是有问题的
解题思路:
很好理解,如果都生病了,那就是1有问题,如果都没生病,那就是4有问题
如果是其中一个生病了,那就判断是哪个生病就行了
代码(C++):
void solve() {
std::string s1, s2;
std::cin >> s1 >> s2;
int ans;
bool f1 = s1[0] == 's', f2 = s2[0] == 's';
if (f1 && f2) {
ans = 1;
} else if (!f1 && !f2) {
ans = 4;
} else {
if (f1) {
ans = 2;
} else {
ans = 3;
}
}
std::cout << ans;
}
题目B:
题目大意:
给你一个字符串,你需要找到(i, j, k)的个数
其中(i, j, k)需要满足下面所有条件:
(1)i < j < k
(2)s[i] == 'A', s[j] == 'B', s[k] == 'C'
(3)j - i == k - j
解题思路:
题目数据给出的字符串s的长度最多为100,所以可以用三个for循环,分别循环i, j, k查找满足条件的个数即可,时间复杂度O(n ^ 3)
但是其实可以再稍微优化一下
既然要满足j - i == k - j,那么我们可以只枚举i和j,然后把k定义成2 j - i即可,然后再判断条件(2)是否满足
时间复杂度O(n ^ 2)
代码(C++):
void solve() {
std::string s1, s2;
std::cin >> s1 >> s2;
int ans;
bool f1 = s1[0] == 's', f2 = s2[0] == 's';
if (f1 && f2) {
ans = 1;
} else if (!f1 && !f2) {
ans = 4;
} else {
if (f1) {
ans = 2;
} else {
ans = 3;
}
}
std::cout << ans;
}
题目C:
题目大意:
现在有一个无向图,我们得把这个图变成简单图,请问最少要删除的边的个数是多少?
简单图的定义:它不包含自环(一个顶点到自身的边)或多边(两个顶点之间有多于一条的边)。
解题思路:
跟图没啥关系
自环可以理解成两个数字相等
多边可以理解成已经有过一个边,也就是这对数字(不计顺序)之前存在过
那么我们可以用一个set即可解决,具体看代码实现
代码(C++):
void solve() {
int n, m;
std::cin >> n >> m;
std::set<std::pair<int, int>> st;
for (int i = 0; i < m; i++) {
int u, v;
std::cin >> u >> v;
if (u == v) { //相等直接跳过就行
continue;
}
if (u > v) { //由于不计顺序,我们每次把大的放前面就行了
std::swap(u, v);
}
st.insert({u, v});
}
std::cout << m - st.size();
}
题目D:
题目大意:
给你一个只包含字符’1‘和字符’0‘的字符串s
在一次操作中,你可以选择字符串s中两个相邻的字符,交换这两个字符
请问让字符串s中所有的1都相邻的最小操作次数是多少?
解题思路:
可以根据我的思路一步一步理解
首先,由于我们只关注1的位置,我们可以把操作理解成:
把字符’0‘想象成一个格子,字符’1‘可以移动到前面一个单位的空格子上,或者后面的
然后,我们可以很容易的想到:要让每一个1相邻,可以以一个’1‘为基准(这个基准不移动),让其他’1‘往这个基准上移动即可
那么问题就变成了,如何找到这个基准,使得操作次数最小?
我们可以用数组pos记录每一个1的位置,可以证明,在这些位置中位于中间的那个作为基准可以使得移动次数最小,也就是pos[pos.size() / 2]这个作为基准,让其他1往基准靠
反证法:如果选择pos.size() / 2左边的,那右边就要移动更多次数才行
具体实现可以看代码实现
代码(C++):
void solve() {
int n;
std::string s;
std::cin >> n >> s;
std::vector<int> pos;
for (int i = 0; i < n; i++) {
if (s[i] == '1') {
pos.push_back(i);
}
}
n = pos.size();
int mid = pos[n / 2];
int cur = 1; //只需要移动到mid相邻的位置,
//并且每次移动后相邻位置都会多一个1
i64 ans = 0; //记得开long long
//计算mid左边的1
for (int i = 0; i < n / 2; i++) {
ans += (mid - cur - pos[i]);
cur++;
}
cur = 1;
//右边
for (int i = n / 2 + 1; i < n; i++) {
ans += (pos[i] - cur - mid);
cur++;
}
std::cout << ans;
}
题目E:
题目大意:
给你一个数组和一个数字k,对于数组中每一个数字ai,你选择数组中包含这个数字ai的k个数字
你需要找出一种选法,使得这k个数字的gcd的最大
解题思路:
根据题目意思,我们确定一个ai,然后再从数组里面找k - 1个数字,使得这k个数字的gcd最大
但是这样的方案数目过于多了
我们可以换一种方向考虑,既然最后求的是gcd,也就是这k个数字的最大公因数。
那么对于每个数字ai,我们枚举ai的因数d,对于每一个因数d,我们查看数组中是否有至少k个数字也有这个因数d,如果有,表示我们可以选择这k个数字,然后答案更新成d
具体怎么实现呢?
对于一个数字ai, 我们需要统计这个数字的因数,然后我们需要查看每个因数是否有k个数字也包含
我们可以用一个数组cnt来统计每一个数字的每一个因数的出现的个数
然后对于ai每个因数d,我们只需要判断cnt[d] >= k,只要大于等于k,那么可以更新答案ans = max(ans, d)
具体可以看实现代码
代码(C++):
const int MAX = 1e6;
//时间瓶颈主要在这里,这个函数的时间复杂度为o(MX*logMX)
std::vector<std::vector<int>> build(int mx) {
std::vector<std::vector<int>> ans(mx + 1);
for (int i = 1; i <= mx; ++i) {
// 遍历每个数字 i,找到它的所有倍数 j
for (int j = i; j <= mx; j += i) {
// 将 i 添加到 j 的因数列表中
ans[j].push_back(i);
}
}
return ans;
}
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
auto arr = build(MAX);
//统计数字d出现的次数,其中数字d是某个数组元素的某个因数
std::vector<int> cnt(MAX + 1, 0);
for (int& x : a) {
//枚举x的因数
for (int& d : arr[x]) {
cnt[d]++;
}
}
for (int i = 0; i < n; i++) {
int ans = 1; //gcd最小值为1
//我们枚举a[i]的每一个因数
for (int& d : arr[a[i]]) {
//可以找到至少k个数字有这个因数就更新答案
if (cnt[d] >= k) {
ans = std::max(ans, d);
}
}
std::cout << ans << "\n";
}
}
题目F:
施工中...