AtCoder Beginner Contest 393 A - F详细题解

本文为AtCoder Beginner Contest 393 A - F详细题解,觉得有帮助或者写的不错可以点个赞,没有打这个比赛的, 也可以收藏起来之后vp看(

目录

题目A:

题目大意:

解题思路:

代码(C++):

题目B:

题目大意:

解题思路:

代码(C++):

题目C:

题目大意:

解题思路:

代码(C++):

题目D:

题目大意:

解题思路:

代码(C++):

题目E:

题目大意:

解题思路:

代码(C++):

题目F:

题目大意:

解题思路:

代码(C++):


题目A:

A - Poisonous Oyster

题目大意:

有四种食物,分别是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:

B - A..B..C

题目大意:

给你一个字符串,你需要找到(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:

C - Make it Simple

题目大意:

现在有一个无向图,我们得把这个图变成简单图,请问最少要删除的边的个数是多少?

简单图的定义:它不包含自环(一个顶点到自身的边)或多边(两个顶点之间有多于一条的边)。

解题思路:

跟图没啥关系
自环可以理解成两个数字相等
多边可以理解成已经有过一个边,也就是这对数字(不计顺序)之前存在过
那么我们可以用一个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:

D - Swap to Gather

题目大意:

给你一个只包含字符’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:

E - GCD of Subset

题目大意:

给你一个数组和一个数字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:

施工中...

题目大意:

解题思路:

代码(C++):

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值