ABC204 A~E
- [A - Rock-paper-scissors](https://atcoder.jp/contests/abc204/tasks/abc204_a)
- [B - Nuts](https://atcoder.jp/contests/abc204/tasks/abc204_b)
- [C - Tour](https://atcoder.jp/contests/abc204/tasks/abc204_c)
- [D - Cooking](https://atcoder.jp/contests/abc204/tasks/abc204_d)
- [E - Rush Hour 2](https://atcoder.jp/contests/abc204/tasks/abc204_e)
A - Rock-paper-scissors
题目大意
三个人玩石头剪刀布平局,其中两个出的分别是 x , y x,y x,y,求另一个人出的。
0 ≤ x , y ≤ 2 0\le x,y\le 2 0≤x,y≤2( 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={x3−x−y(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,Ai−10)颗果实。他一共会得到多少果实?
1 ≤ N , A i ≤ 1000 1\le N,A_i\le 1000 1≤N,Ai≤1000
输入格式
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
X→Y和
Y
→
X
Y\to X
Y→X算作两种不同的方案。
2
≤
N
≤
2000
2\le N\le 2000
2≤N≤2000
0
≤
M
≤
min
(
2000
,
N
(
N
−
1
)
)
0\le M\le \min(2000,N(N-1))
0≤M≤min(2000,N(N−1))
1
≤
A
i
,
B
i
≤
N
1\le A_i,B_i\le N
1≤Ai,Bi≤N
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 1→1、 1 → 2 1\to2 1→2、 1 → 3 1\to3 1→3、 2 → 2 2\to2 2→2、 2 → 3 2\to3 2→3、 3 → 2 3\to2 3→2、 3 → 3 3\to3 3→3。
样例输入2
3 0
样例输出2
3
有三种可行的旅游方案: 1 → 1 1\to1 1→1、 2 → 2 2\to2 2→2、 3 → 3 3\to3 3→3。
分析
我们可以把这个国家看成一个简单有向无权图,并把每个节点作为起点跑一遍
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
1≤N≤100
1
≤
T
i
≤
1
0
3
1\le T_i\le 10^3
1≤Ti≤103
输入格式
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|
∣∑A−∑B∣必须也达到最小。
我们可以将
∑
T
\sum T
∑T分成两半,其中一半为
⌊
∑
T
2
⌋
\lfloor\frac{\sum T}2\rfloor
⌊2∑T⌋。这时,我们可以用dp背包解决此题:从
T
T
T中选出一个子序列
A
A
A,使得
∑
A
≤
⌊
∑
T
2
⌋
\sum A\le\lfloor\frac{\sum T}2\rfloor
∑A≤⌊2∑T⌋,这样答案就为
∑
T
−
∑
A
\sum T-\sum A
∑T−∑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
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
2≤N≤105
2
≤
M
≤
1
0
5
2\le M\le 10^5
2≤M≤105
1
≤
A
i
,
B
i
≤
N
1\le A_i,B_i\le N
1≤Ai,Bi≤N
0
≤
C
i
,
D
i
≤
1
0
9
0\le C_i,D_i\le 10^9
0≤Ci,Di≤109
输入格式
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;
}