Educational Codeforces Round 109 div2 A~F题解
Posted 欣君
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Educational Codeforces Round 109 div2 A~F题解相关的知识,希望对你有一定的参考价值。
视频讲解:BV1MU4y1L7FB
A. Potion-making
题目大意
需要调配出精华浓度为 k % k\\% k% 的药剂,每次可以添加 1 1 1 升水或 1 1 1 升精华,求最少需要添加几次。 k k k 为 [ 1 , 100 ] [1,100] [1,100] 范围内的整数。
题解
调配出精华浓度为
k
%
k\\%
k% 的药剂,即水与精华的比例
a
b
=
100
−
k
k
\\frac{a}{b}=\\frac{100-k}{k}
ba=k100−k 。易得当
a
b
\\frac{a}{b}
ba 为最简分数,
a
+
b
a+b
a+b 最小。
即
a
=
100
−
k
g
c
d
(
k
,
100
−
k
)
a=\\frac{100-k}{gcd(k,100-k)}
a=gcd(k,100−k)100−k ,
b
=
k
g
c
d
(
k
,
100
−
k
)
b=\\frac{k}{gcd(k,100-k)}
b=gcd(k,100−k)k 。
特别的,当
k
=
100
k=100
k=100 时, 应取
a
=
1
,
b
=
0
a=1,b=0
a=1,b=0 。在部分求解最大公约数的代码写法里,可能会出现错误结果。
参考代码
#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b)
{
return a%b?gcd(b,a%b):b;
}
int main()
{
int T,k,g,ans;
scanf("%d",&T);
while(T--)
{
scanf("%d",&k);
g=gcd(100-k,k);
ans=k/g+(100-k)/g;
printf("%d\\n",ans);
}
}
B. Permutation Sort
题目大意
给定一个
1
1
1 到
n
n
n 的排列
a
[
]
a[]
a[] ,每次操作可以选择一个子区间(但不能选择整个排列),随意调整该区间内数的顺序。
求最少需要操作几次,使得排列
a
[
]
a[]
a[] 变为递增序列。
题解
因为子区间可以随意选取,不妨假设每次都选取一个长度为 n − 1 n-1 n−1 的区间,即选择区间 [ 1 , n − 1 ] [1,n-1] [1,n−1] 或区间 [ 2 , n ] [2,n] [2,n]。
接下来分几种情况讨论:
- 若原排列就是一个递增序列,则无需修改,共操作 0 0 0 次;
- 若 a 1 = 1 a_1=1 a1=1 或 a n = n a_n=n an=n ,则只需要将区间 [ 2 , n ] [2,n] [2,n] 或区间 [ 1 , n − 1 ] [1,n-1] [1,n−1] 排序即可,共操作 1 1 1 次;
- 若 a 1 = n a_1=n a1=n 且 a n = 1 a_n=1 an=1 ,则需要先操作 2 2 2 次,将 1 1 1 放到 a 1 a_1 a1 上(或将 n n n 放到 a n a_n an 上),再操作 1 1 1 次将剩余数排序,共操作 3 3 3 次;
- 对于其他情况,则操作一次将 1 1 1 放到 a 1 a_1 a1 上(或将 n n n 放到 a n a_n an 上),再操作 1 1 1 次将剩余数排序,共操作 2 2 2 次;
参考代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=60;
int a[MAXN];
int main()
{
int T,n,flag,i;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
flag=1;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]!=i)
flag=0;
}
if(flag)
printf("0\\n");
else if(a[1]==1||a[n]==n)
printf("1\\n");
else if(a[1]==n&&a[n]==1)
printf("3\\n");
else
printf("2\\n");
}
}
C. Robot Collisions
题目大意
有
n
n
n 个机器人在数轴上。数轴上坐标为
0
0
0 和
m
m
m 的位置为墙壁。
第
i
i
i 个机器人初始在整数坐标
x
i
(
0
<
i
<
m
)
x_i(0< i < m)
xi(0<i<m) 上,并朝左(朝向原点)或朝右(朝向无穷大)以每秒
1
1
1 个单位的速度前进。机器人初始所在位置不重复。
每当机器人抵达墙壁时,会立刻调转方向,以同样的速度沿相反方向前进。
每当两个机器人同时抵达同一整数坐标时,他们会发生碰撞并爆炸。如果在非整数坐标相遇,则不会爆炸。
求每个机器人是否会爆炸,若会则输出爆炸时刻,否则输出
−
1
-1
−1 。
题解
首先注意到一点,就是只有在整数坐标相遇时才会爆炸。因此可以把所有机器人按照初始坐标的奇偶性分为两类,只有相同奇偶性坐标的机器人之间才有可能碰撞。
接下来考虑相同奇偶性的初始坐标的机器人之间的碰撞的问题。
如果不考虑两侧的墙,会发现这就是一个左右括号匹配的问题。
- 括号匹配问题:给定一个由左右括号构成的字符串,求合法的最大左右括号匹配数。通常采用栈来解决。
朝右走的机器人代表左括号,朝左走的机器人代表右括号,求这个括号字符串直接的相互匹配关系。这个问题可以用栈解决。碰到左括号则将其放入栈中,碰到右括号则将其与栈顶的左括号匹配(没有则无配对)。
然后加入对墙壁的考虑。如果当前栈空,即当前机器人左侧没有其他机器人,或者左侧的机器人都两两碰撞消灭掉了(即括号均匹配成功),那么即使当前机器人是朝左走,也是可以加入栈中与其他朝左走的机器人进行匹配的。因为当前机器人朝左走到头后撞墙,会调转方向变为朝右走。即对于最左侧的机器人来说,不管其初始是朝左走还是朝右走,都可以变化为朝右走。这样就可以部分解决撞墙问题了。
若最左侧尚未匹配的机器人位于
x
i
x_i
xi 坐标朝左走,我们可以将坐标
0
0
0 处的墙壁当作镜子,将其等价于位于
−
x
i
-x_i
−xi 坐标朝右走。这样就能作为左括号加入栈中参与匹配。
如上从左到右遍历后,可能会出现栈中元素个数
≥
2
\\geq2
≥2 的情况,即有至少
2
2
2 个机器人是朝右走的。那么我们可以不断取出栈顶的两个元素,即最右侧的两个机器人,他们必会在最右侧的机器人撞墙反向后,相互发生碰撞。
同样将坐标
m
m
m 处的墙当作镜子,位于
x
i
x_i
xi 坐标的机器人向右走,等价于位于
2
m
−
x
i
2m-x_i
2m−xi 坐标的机器人向左走。
这样不断取出栈顶两个元素,计算他们的碰撞时刻,直到栈中的元素个数
≤
1
\\leq 1
≤1,即只剩最多
1
1
1 个机器人时,结束计算。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=300300;
struct Robot
{
int id,x;
char dir;
bool operator <(Robot r)
{
return x<r.x;
}
}a[MAXN],stk[MAXN];
int m;
int ans[MAXN];
vector<Robot> v0,v1;
void cal(vector<Robot>& vec)
{
int cnt=0,i;
for(i=0;i<vec.size();i++)
{
if(cnt==0||vec[i].dir=='R')
{
if(vec[i].dir=='L')
vec[i].x=-vec[i].x;
stk[cnt++]=vec[i];
}
else
{
Robot pre=stk[--cnt];
ans[vec[i].id]=ans[pre.id]=(vec[i].x-pre.x)/2;
}
}
while(cnt>=2)
{
Robot r=stk[--cnt];
Robot l=stk[--cnt]Educational Codeforces Round 7 A
Educational Codeforces Round 7
Educational Codeforces Round 90
Educational Codeforces Round 33