AtCoder Beginner Contest 368 题解&&思路(A-D,F)

AtCoder Beginner Contest 368 题解&&思路(A-D,F)

A - Cut

题目描述

N N N 个数在一个桶里面,从上往下第 i i i 个数是 A i A_i Ai,从桶下面取出 K K K 个数,保持原顺序放在桶的上面,从上到下打印写在卡片上的整数。

思路

时间复杂度 O ( N ) . \mathcal{O}(N). O(N).

本质上就是先输出后 K K K 个数然后再从前面输出。

#include <iostream>
#include <cstdio>
#include <stdlib.h>
#define N 105

using namespace std;
int a[N];
signed main(){
	int n,k;
	cin >> n >> k;
	for (int i = 1;i <= n;i ++) cin >> a[i];
	for (int i = n - k + 1;i <= n;i ++) cout << a[i] << ' ';
	for (int i = 1;i < n - k + 1;i ++) cout << a[i] << ' ';
	return 0;
}

B - Decrease 2 max elements

题目描述

问题陈述

给你一个由 N N N 个正整数 A = ( A 1 , A 2 , … , A N ) A = (A_1, A_2, \dots ,A_N) A=(A1,A2,,AN) 组成的序列。高桥重复下面的操作,直到 A A A 包含一个或更少的正整数元素:

  • 按降序排列 A A A 。然后将 A 1 A_1 A1 A 2 A_2 A2 减少 1 1 1

求他执行此操作的次数。

思路

直接模拟即可。

用数组或者 m a p map map 存一下大于 0 0 0 的有多少个就行,每次操作后更新。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stdlib.h>
#include <map>
#define N 105
using namespace std;
int n,a[N],ans;
map<bool,int> mp; 
signed main(){
	cin >> n;
	for (int i = 1;i <= n;i ++) cin >> a[i];
	for (int i = 1;i <= n;i ++) {
		if (a[i] > 0) mp[1] ++;
		else mp[0] ++;
	}
	while(mp[1] > 1) {
		stable_sort(a + 1,a + 1 + n,[](int x,int y) {
			return x > y;
		});
		a[1] --,a[2] --;
		if (a[1] == 0) mp[1]--,mp[0] ++;
		if (a[2] == 0) mp[1]--,mp[0] ++;
		ans ++;
	}
	cout << ans;
	return 0;
}

C - Triple Attack

题目描述

问题陈述

你正在玩一个游戏。

N N N 个敌人排成一排,最前面的 i i i 个敌人的生命值是 H i H_i Hi

你将使用初始化为 0 0 0 的变量 T T T 重复以下操作,直到所有敌人的生命值都变为 0 0 0 或更少。

  • T T T 增加 1 1 1 。然后,攻击最前方生命值为 1 1 1 或更高的敌人。如果 T T T 3 3 3 的倍数,敌人的生命值会减少 3 3 3 ;否则,生命值会减少 1 1 1

当所有敌人的生命值变为 0 0 0 或更少时,求 T T T 的值。

思路

赛时有细节问题,没打出来,呜呜呜呜

我们可以发现一个循环节:
T = 3 , 4 , 5 T=3,4,5 T=3,4,5
这里攻击对方是 5 5 5 的健康值,而只用迭代 3 3 3 T . T. T.

对于没有到 T % 3 = 0 T\%3=0 T%3=0 的,暴力枚举即可(我赛时就是因为没有这样,想用 O ( 1 ) O(1) O(1) 解决,导致有一大堆细节问题……)。

其他的就不必多说了。

#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
#define N 200005
#define int long long
using namespace std;
int n,a[N],T = 0;
signed main(){
	cin >> n;
	for (int i = 1;i <= n;i ++) cin >> a[i];
	for (int i = 1;i <= n;i ++) {
		while(a[i] > 0 && T % 3) {
			T ++;
			if (T % 3) a[i]--;
			else a[i] -= 3;
		}
		if (a[i] > 0) {
			int m = a[i] / 5;
			T += m * 3;
			a[i] %= 5;
			if (a[i] >= 1) T ++;
			if (a[i] >= 2) T ++;
			if (a[i] >= 3) T ++;
		}
	}
	cout << T;
	return 0;
}

D - Minimum Steiner Tree

题目描述

问题陈述

给你一棵树,树上有 N N N 个顶点,编号为 1 1 1 N N N 。第 i i i 条边连接顶点 A i A_i Ai B i B_i Bi

考虑从这个图中删除一些边和顶点(可能为零)后可以得到一棵树。求这样一棵树中包含所有 K K K 指定顶点 V 1 , … , V K V_1,\ldots,V_K V1,,VK 的顶点的最小数目。

思路

注意到题目给的是一棵树,也就是说从 u u u v v v 有且仅有一条路径,难道这里我们要枚举 V i , V j V_i,V_j Vi,Vj ?其实不用,我们可以从 V 1 V_1 V1 为根开始 dfs 图的遍历,碰到节点 x ∈ V x\in V xV 的,就更新这条路径上的点(表示算作答案),但一定要先遍历再更新(有可能当前节点 u ∈ V u\in V uV x ∈ V x\in V xV x x x u u u 子树下的一个点,这样就会顺便把 V 1 → u V_1\rightarrow u V1u 的路径也给更新了)。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <stdlib.h>
#include <vector>
#include <set>
#define N 200005

using namespace std;
int n,v[N],k;
vector<int> g[N];
bool bj[N];
bool vis[N];
int stk[N],tp;
void dfs(int cur,int fa) {
	tp ++,stk[tp] = cur;
	for (auto i : g[cur])
		if (i != fa){
			dfs(i,cur);
			if (bj[i] && !vis[i]) {
				for (int j = 1;j <= tp;j ++) vis[stk[j]] = 1;
				vis[i] = 1;
			}
		}
	tp --;
}
signed main(){
	cin >> n >> k;
	for (int i = 1;i < n;i ++) {
		int u,v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for (int i = 1;i <= k;i ++) cin >> v[i],bj[v[i]] = 1;
	dfs(v[1],0);
	vis[v[1]] = 1;
	int ans = 0;
	for (int i = 1;i <= n;i ++) ans += vis[i];
	cout << ans;
	return 0;
}

E - Train Delay

题目描述

问题陈述

在 Atcoder 国家,有 N N N 座城市,编号为 1 1 1 N N N ,以及 M M M 列火车,编号为 1 1 1 M M M i i i 次列车在 S i S_i Si 点从城市 A i A_i Ai 出发,在 T i T_i Ti 点到达城市 B i B_i Bi

给定一个正整数 X 1 X_1 X1 ,求一个非负整数 X 2 , … , X M X_2,\ldots,X_M X2,,XM 的最小值 X 2 + … + X M X_2+\ldots+X_M X2++XM ,以满足下列条件。

  • 条件对于所有满足 1 ≤ i , j ≤ M 1 \leq i,j \leq M 1i,jM 的一对 ( i , j ) (i,j) (i,j) ,如果 B i = A j B_i=A_j Bi=Aj T i ≤ S j T_i \leq S_j TiSj ,那么 T i + X i ≤ S j + X j T_i+X_i \leq S_j+X_j Ti+XiSj+Xj
    • 换句话说,对于任何一对原本可以换乘的列车,即使将每趟列车 i i i 的出发和到达时间延迟 X i X_i Xi ,仍然可以换乘。

可以证明,这种将 X 2 , … , X M X_2,\ldots,X_M X2,,XM 设置为 X 2 + … + X M X_2+\ldots+X_M X2++XM 的最小值的方法是唯一的。

思路

赛场上没有想出来就先做 T 6 T_6 T6 了,后面更。

F - Dividing Game

题目描述

问题陈述

给你一个由 N N N 个正整数 A = ( A 1 , A 2 , … , A N ) A = (A_1, A_2, \dots ,A_N) A=(A1,A2,,AN) 组成的序列,其中每个元素至少是 2 2 2 。安娜和布鲁诺用这些整数玩一个游戏。他们轮流执行以下操作,安娜先执行。

  • 自由选择一个整数 i   ( 1 ≤ i ≤ N ) i \ (1 \leq i \leq N) i (1iN) 。然后,自由选择 A i A_i Ai 的一个不是 A i A_i Ai 本身的正整除 x x x ,并将 A i A_i Ai 替换为 x x x

不能进行操作的一方输,另一方赢。假设两位棋手都以最佳的方式下棋,那么谁会赢呢?

思路

这是道博弈论的题目,不难想出来这题应该和因数有关,考虑时间复杂度 O ( N N ) \mathcal{O}(N\sqrt N) O(NN ) 的算法。

看到此题我先求出其因数的个数(除了它本身),然后,观察到现在是不是跟之前做过的题目一样,没错是它!

——Nim 游戏

但交上去,挂了……

现在我考虑这条关于 A i A_i Ai 的因子链,发现跟它的最长链有关,不过也很好理解,拿最长能够使先手必胜的状态更大,于是就 A A A 了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 100005
using namespace std;
int n,a[N],cnt;
signed main(){
	cin >> n;
	for (int i = 1;i <= n;i ++) {
		cin >> a[i];
		int p = 0;
		for (int j = 2;j * j <= a[i];j ++)
			while(a[i] % j == 0) a[i] /= j,p ++;
		if (a[i] > 1) p ++;
		cnt ^= p;
//		cout << p << endl;
	}
	if (cnt == 0) return cout << "Bruno",0;
	cout << "Anna"; 
	return 0;
}
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值