Loading

AtCoder Beginner Contest 204 A~E 题解

A - Rock-paper-scissors

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

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

输入格式

\(x,y\)

输出格式

用整数格式输出答案。

样例

\(x\) \(y\) 输出
\(0\) \(1\) \(2\)
\(0\) \(0\) \(0\)

分析

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

  • \(x=y=z\)
  • \(x\ne y\ne z\)

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

代码

#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\)棵树,第\(i\)棵树上有\(A_i\)颗果实。
一个人会从第\(i\)棵树摘下\(\max(0,A_i-10)\)颗果实。他一共会得到多少果实?

\(1\le N,A_i\le 1000\)

输入格式

\(N\)
\(A_1~\dots~A_N\)

输出格式

输出答案。

样例

样例输入1

3
6 17 28

样例输出1

25

他会从三棵树上分别摘下\(0,7,18\)颗果实,总共\(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\)\(N\)\(N\)座城市和编号为\(1\)\(M\)\(M\)条路。
\(i\)条路从城市\(A_i\)\(B_i\),且不能用它从城市\(B_i\)\(A_i\)
一个人想从起点城市开始旅行并走过若干条路(可以不走,即只游玩一个城市)并到达终点城市。
有多少种合法的起点和终点城市?如果\(X\ne Y\),则\(X\to Y\)\(Y\to X\)算作两种不同的方案。

\(2\le N\le 2000\)
\(0\le M\le \min(2000,N(N-1))\)
\(1\le A_i,B_i\le N\)
\(A_i\ne B_i\)
\((A_i,B_i)\)互不相同。

输入格式

\(N~M\)
\(A_1~B_1\)
\(\vdots\)
\(A_M~B_M\)

输出格式

输出答案。

样例

样例输入1

3 3
1 2
2 3
3 2

样例输出1

7

有七种可行的旅游方案:\(1\to1\)\(1\to2\)\(1\to3\)\(2\to2\)\(2\to3\)\(3\to2\)\(3\to3\)

样例输入2

3 0

样例输出2

3

有三种可行的旅游方案:\(1\to1\)\(2\to2\)\(3\to3\)

分析

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

代码

注意:每次\(\text{DFS}\)之前一定要将\(\mathrm{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\)个碗,其中任意一个人洗第\(i\)个碗需要\(T_i\)分钟。一个人不能同时洗多个碗。
两个人一起洗碗,洗完所有碗至少需要多少分钟?

\(1\le N\le 100\)
\(1\le T_i\le 10^3\)

输入格式

\(N\)
\(T_1~T_2~\dots~T_N\)

输出格式

输出答案。

样例

样例输入1

5
8 3 7 2 5

样例输出1

13

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

  • 第一个人洗编号为\(5,1\)的碗。总时间为\(T_5+T_1=13\)分钟。
  • 第二个人洗编号为\(2,4,3\)的碗。总时间为\(T_2+T_4+T_3=10\)分钟。

总耗时为\(\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\),将其分成两个子序列\(A\)\(B\)(不要求连续),求最小的\(\min(\sum A,\sum B)\)
这时,我们发现要使\(\min(\sum A,\sum B)\)最小,由于\(\sum A+\sum B=\sum T\),所以\(|\sum A-\sum B|\)必须也达到最小。
我们可以将\(\sum T\)分成两半,其中一半为\(\lfloor\frac{\sum T}2\rfloor\)。这时,我们可以用dp背包解决此题:从\(T\)中选出一个子序列\(A\),使得\(\sum A\le\lfloor\frac{\sum T}2\rfloor\),这样答案就为\(\sum T-\sum A\)

代码

#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\)座城市和\(M\)条路。城市的编号是\(1\)\(N\),路的编号则为\(1\)\(M\)。第\(i\)条路双向连接着城市\(A_i\)\(B_i\)
在这个国家中,初始时间为\(0\)。如果你在时间点\(t\)通过公路\(i\),所需时间为\(C_i+\lfloor\frac {D_i} {t+1}\rfloor\)
一个人想从城市\(1\)到达城市\(N\)。他在每个城市可以停留任意自然数的时间。
求这个人最早到达城市\(N\)的时间。如果无法到达城市\(N\),输出-1

\(2\le N\le 10^5\)
\(2\le M\le 10^5\)
\(1\le A_i,B_i\le N\)
\(0\le C_i,D_i\le 10^9\)

输入格式

\(N~M\)
\(A_1~B_1~C_1~D_1\)
\(\vdots\)
\(A_M~B_M~C_M~D_M\)

输出格式

输出答案。

样例

样例输入1

2 1
1 2 2 3

样例输出1

4

我们可以先在城市\(1\)停留至时间\(1\)。然后,我们出发,最终到达时间\(1+2+\lfloor\frac 3 {1+1}\rfloor=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\)到城市\(N\)可能没有路径。

分析

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

代码

#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;
}
posted @ 2021-06-07 19:02  GoodCoder666  阅读(45)  评论(0)    收藏  举报