AtCoder Beginner Contest 204 A~E 题解

46 篇文章 7 订阅
43 篇文章 6 订阅

A - Rock-paper-scissors

题目大意

三个人玩石头剪刀布平局,其中两个出的分别是 x , y x,y x,y,求另一个人出的。

0 ≤ x , y ≤ 2 0\le x,y\le 2 0x,y2 0 , 1 , 2 0,1,2 0,1,2分别表示石头,剪刀,布)

输入格式

x , y x,y x,y

输出格式

用整数格式输出答案。

样例

x x x y y y输出
0 0 0 1 1 1 2 2 2
0 0 0 0 0 0 0 0 0

分析

石头剪刀布这个游戏三人平局只有两种情况(设 z z z为第三个人出的):

  • x = y = z x=y=z x=y=z
  • x ≠ y ≠ z x\ne y\ne z x=y=z

所以,我们得出如下递推式:
z = { x ( x = y ) 3 − x − y ( x ≠ y ) z=\begin{cases}x & (x=y)\\3-x-y & (x\ne y)\end{cases} z={x3xy(x=y)(x=y)

代码

#include <cstdio>
using namespace std;

int main()
{
	int x, y;
	scanf("%d%d", &x, &y);
	printf("%d\n", x == y? x: 3 - x - y);
	return 0;
}

B - Nuts

题目大意

N N N棵树,第 i i i棵树上有 A i A_i Ai颗果实。
一个人会从第 i i i棵树摘下 max ⁡ ( 0 , A i − 10 ) \max(0,A_i-10) max(0,Ai10)颗果实。他一共会得到多少果实?

1 ≤ N , A i ≤ 1000 1\le N,A_i\le 1000 1N,Ai1000

输入格式

N N N
A 1   …   A N A_1~\dots~A_N A1  AN

输出格式

输出答案。

样例

样例输入1

3
6 17 28

样例输出1

25

他会从三棵树上分别摘下 0 , 7 , 18 0,7,18 0,7,18颗果实,总共 25 25 25颗。

样例输入2

4
8 9 10 11

样例输出2

1

他只会从最后一棵树上得到一颗果实。

分析

我们直接按题意模拟即可。

代码

#include <cstdio>
using namespace std;

int main()
{
	int n, ans = 0;
	scanf("%d", &n);
	while(n--)
	{
		int a;
		scanf("%d", &a);
		if(a > 10) ans += a - 10;
	}
	printf("%d\n", ans);
	return 0;
}

C - Tour

题目大意

一个国家有编号为 1 1 1 N N N N N N座城市和编号为 1 1 1 M M M M M M条路。
i i i条路从城市 A i A_i Ai B i B_i Bi,且不能用它从城市 B i B_i Bi A i A_i Ai
一个人想从起点城市开始旅行并走过若干条路(可以不走,即只游玩一个城市)并到达终点城市。
有多少种合法的起点和终点城市?如果 X ≠ Y X\ne Y X=Y,则 X → Y X\to Y XY Y → X Y\to X YX算作两种不同的方案。

2 ≤ N ≤ 2000 2\le N\le 2000 2N2000
0 ≤ M ≤ min ⁡ ( 2000 , N ( N − 1 ) ) 0\le M\le \min(2000,N(N-1)) 0Mmin(2000,N(N1))
1 ≤ A i , B i ≤ N 1\le A_i,B_i\le N 1Ai,BiN
A i ≠ B i A_i\ne B_i Ai=Bi
( A i , B i ) (A_i,B_i) (Ai,Bi)互不相同。

输入格式

N   M N~M N M
A 1   B 1 A_1~B_1 A1 B1
⋮ \vdots
A M   B M A_M~B_M AM BM

输出格式

输出答案。

样例

样例输入1

3 3
1 2
2 3
3 2

样例输出1

7

有七种可行的旅游方案: 1 → 1 1\to1 11 1 → 2 1\to2 12 1 → 3 1\to3 13 2 → 2 2\to2 22 2 → 3 2\to3 23 3 → 2 3\to2 32 3 → 3 3\to3 33

样例输入2

3 0

样例输出2

3

有三种可行的旅游方案: 1 → 1 1\to1 11 2 → 2 2\to2 22 3 → 3 3\to3 33

分析

我们可以把这个国家看成一个简单有向无权图,并把每个节点作为起点跑一遍 DFS \text{DFS} DFS,计算总共能到达的结点数即可。
总时间复杂度为 O ( n 2 ) \mathcal O(n^2) O(n2)

代码

注意:每次 DFS \text{DFS} DFS之前一定要将 v i s \mathrm{vis} vis数组清零!

#include <cstdio>
#include <vector>
#define maxn 2005
using namespace std;

vector<int> G[maxn];
bool vis[maxn];

int ans;

void dfs(int v)
{
	if(vis[v]) return;
	vis[v] = true, ans ++;
	for(int u: G[v])
		dfs(u);
}

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	while(m--)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		G[--x].push_back(--y);
	}
	ans = 0;
	for(int i=0; i<n; i++)
	{
		for(int j=0; j<n; j++)
			vis[j] = false;
		dfs(i);
	}
	printf("%d\n", ans);
	return 0;
}

D - Cooking

题目大意

两个人要洗 N N N个碗,其中任意一个人洗第 i i i个碗需要 T i T_i Ti分钟。一个人不能同时洗多个碗。
两个人一起洗碗,洗完所有碗至少需要多少分钟?

1 ≤ N ≤ 100 1\le N\le 100 1N100
1 ≤ T i ≤ 1 0 3 1\le T_i\le 10^3 1Ti103

输入格式

N N N
T 1   T 2   …   T N T_1~T_2~\dots~T_N T1 T2  TN

输出格式

输出答案。

样例

样例输入1

5
8 3 7 2 5

样例输出1

13

我们可以让两个人分别洗如下的碗:

  • 第一个人洗编号为 5 , 1 5,1 5,1的碗。总时间为 T 5 + T 1 = 13 T_5+T_1=13 T5+T1=13分钟。
  • 第二个人洗编号为 2 , 4 , 3 2,4,3 2,4,3的碗。总时间为 T 2 + T 4 + T 3 = 10 T_2+T_4+T_3=10 T2+T4+T3=10分钟。

总耗时为 max ⁡ ( 13 , 10 ) = 13 \max(13,10)=13 max(13,10)=13分钟。

样例输入2

2
1000 1

样例输出2

1000

样例输入3

9
3 14 15 9 26 5 35 89 79

样例输出3

138

分析

这是一道经典01背包题。
题目可以直接描述为:给定序列 T T T,将其分成两个子序列 A A A B B B(不要求连续),求最小的 min ⁡ ( ∑ A , ∑ B ) \min(\sum A,\sum B) min(A,B)
这时,我们发现要使 min ⁡ ( ∑ A , ∑ B ) \min(\sum A,\sum B) min(A,B)最小,由于 ∑ A + ∑ B = ∑ T \sum A+\sum B=\sum T A+B=T,所以 ∣ ∑ A − ∑ B ∣ |\sum A-\sum B| AB必须也达到最小。
我们可以将 ∑ T \sum T T分成两半,其中一半为 ⌊ ∑ T 2 ⌋ \lfloor\frac{\sum T}2\rfloor 2T。这时,我们可以用dp背包解决此题:从 T T T中选出一个子序列 A A A,使得 ∑ A ≤ ⌊ ∑ T 2 ⌋ \sum A\le\lfloor\frac{\sum T}2\rfloor A2T,这样答案就为 ∑ T − ∑ A \sum T-\sum A TA

代码

#include <cstdio>
#define maxn 105
#define maxv 100005
using namespace std;

int dp[maxv], w[maxn];

inline void setmax(int& x, int y)
{
	if(y > x) x = y;
}

int main()
{
	int n, v = 0;
	scanf("%d", &n);
	for(int i=0; i<n; i++)
	{
		scanf("%d", w + i);
		v += w[i];
	}
	int t = v;
	v >>= 1;
	for(int i=0; i<n; i++)
		for(int j=v; j>=w[i]; j--)
			setmax(dp[j], dp[j - w[i]] + w[i]);
	printf("%d\n", t - dp[v]);
	return 0;
}

E - Rush Hour 2

题目大意

一个国家有 N N N座城市和 M M M条路。城市的编号是 1 1 1 N N N,路的编号则为 1 1 1 M M M。第 i i i条路双向连接着城市 A i A_i Ai B i B_i Bi
在这个国家中,初始时间为 0 0 0。如果你在时间点 t t t通过公路 i i i,所需时间为 C i + ⌊ D i t + 1 ⌋ C_i+\lfloor\frac {D_i} {t+1}\rfloor Ci+t+1Di
一个人想从城市 1 1 1到达城市 N N N。他在每个城市可以停留任意自然数的时间。
求这个人最早到达城市 N N N的时间。如果无法到达城市 N N N,输出-1

2 ≤ N ≤ 1 0 5 2\le N\le 10^5 2N105
2 ≤ M ≤ 1 0 5 2\le M\le 10^5 2M105
1 ≤ A i , B i ≤ N 1\le A_i,B_i\le N 1Ai,BiN
0 ≤ C i , D i ≤ 1 0 9 0\le C_i,D_i\le 10^9 0Ci,Di109

输入格式

N   M N~M N M
A 1   B 1   C 1   D 1 A_1~B_1~C_1~D_1 A1 B1 C1 D1
⋮ \vdots
A M   B M   C M   D M A_M~B_M~C_M~D_M AM BM CM DM

输出格式

输出答案。

样例

样例输入1

2 1
1 2 2 3

样例输出1

4

我们可以先在城市 1 1 1停留至时间 1 1 1。然后,我们出发,最终到达时间 1 + 2 + ⌊ 3 1 + 1 ⌋ = 4 1+2+\lfloor\frac 3 {1+1}\rfloor=4 1+2+1+13=4

样例输入2

2 3
1 2 2 3
1 2 2 1
1 1 1 1

样例输出2

3

两个城市之间可能有多条路,一个城市也可能有到自己的路。

样例输入3

4 2
1 2 3 4
3 4 5 6

样例输出3

-1

城市 1 1 1到城市 N N N可能没有路径。

分析

我们可以把输入当成一幅无向图。其实,从一个城市到它自己的路根本没有用,所以我们直接忽略不计。
如果目前时间为 T T T且我们要从城市 A i A_i Ai B i B_i Bi,我们可以证明,最好的整数出发时间应该是 ⌊ D ⌉ − 1 \lfloor\sqrt D\rceil-1 D 1 ⌊ x ⌉ \lfloor x\rceil x表示 x x x四舍五入)。
如果 ⌊ D ⌉ ≤ T \lfloor\sqrt D\rceil\le T D T,我们应该等到时间 ⌊ D ⌉ − 1 \lfloor\sqrt D\rceil-1 D 1再出发;否则,我们直接出发。
这时,我们可以使用Dijkstra最短路算法(使用优先队列优化),这样总时间复杂度就为 O ( M log ⁡ N ) \mathcal O(M\log N) O(MlogN)

代码

#include <cstdio>
#include <cmath>
#include <vector>
#include <queue>
#include <tuple>
#define maxn 100005
#define INF 9223372036854775807LL
using namespace std;

using Road = tuple<int, int, int>;
using LL = long long;
using pli = pair<LL, int>;

vector<Road> G[maxn];
LL dist[maxn];

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	while(m--)
	{
		int a, b, c, d;
		scanf("%d%d%d%d", &a, &b, &c, &d);
		if(--a == --b) continue;
		G[a].emplace_back(b, c, d);
		G[b].emplace_back(a, c, d);
	}
	dist[0] = 0LL;
	for(int i=1; i<n; i++) dist[i] = INF;
	priority_queue<pli, vector<pli>, greater<pli>> q;
	q.emplace(0LL, 0);
	while(!q.empty())
	{
		auto [t, u] = q.top(); q.pop();
		if(dist[u] != t) continue;
		for(auto [v, c, d]: G[u])
		{
			LL t2 = sqrt((long double) d) - 0.5;
			if(t2 < t) t2 = t;
			t2 += LL(c) + LL(d) / (t2 + 1LL);
			if(t2 < dist[v])
				q.emplace(dist[v] = t2, v);
		}
	}
	if(dist[n - 1] == INF) puts("-1");
	else printf("%lld\n", dist[n - 1]);
	return 0;
}
  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
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$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值