AtCoder Beginner Contest 376 A~C
A - Candy Button
洛谷链接
AtCoder链接
题目
有一个按钮,按下这个按钮时,你会得到一颗糖果,每次得到糖果的时间距离你上次得到糖果的时间大于等于
高桥决定按这个按钮
他会得到多少颗糖果?
代码
#include <bits/stdc++.h>
using namespace std;
int n, c, a, cnt = 1, x;
int main()
{
scanf("%d %d\n%d", &n, &c, &x);
for (int i = 1; i < n; i ++ )
{
scanf("%d", &a);
if (a - x >= c)
{
cnt ++;
x = a;
}
}
cout << cnt << '\n';
return 0;
}
B - Hands on Ring (Easy)
洛谷链接
AtCoder链接
题目
注:本问题的设置与问题 F 几乎相同,只有正文中粗体部分和限制条件不同。
你用双手拿着一个戒指。这个戒指由编号为
最初,您的左手拿着部件
- 将其中一只手移动到当前所持部件的相邻部分。但是,只有当另一只手不在目标部件上时才能这样做。
下图显示了初始状态以及可以和不可以进行的操作示例。写在圆环各部分上的数字代表零件编号,标有 L 和 R 的圆圈分别代表你的左手和右手。
你需要按照
- 执行一定数量的操作(可能为零),使您的左手(如果
H_i 为L)或右手(如果H_i 为R)握住T_i 部分。在此,您不得移动H_i 未指定的手。
保证只给出可实现的指令。
求遵循所有指令所需的最小运算总数。
思路
按顺序扫描
- 给你一个整数
from,to 和ng ,每个整数都在1 和N 之间。从from 部分到to 部分的循环需要走多少步?不经过部分ng ?(保证from \ne ng 和to \ne ng )。
这个问题可以根据
首先,我们可以自由交换
剩下的就是
对于前者,最佳的下法是
对于后者,最佳移动方式是
这样,解决问题总共只需
代码
#include <bits/stdc++.h>
using namespace std;
int n, q, l = 1, r = 2, ans, t;
char h;
int num_move(int n, int from, int to, int ng)
{
if (from > to)
swap(from, to);
if (from < ng and ng < to)
return n + from - to;
else
return to - from;
}
int main()
{
scanf("%d %d", &n, &q);
for (int i = 0; i < q; i ++ )
{
cin >> h;
scanf("%d", &t);
if (h == 'L')
{
ans += num_move(n, l, t, r);
l = t;
}
else
{
ans += num_move(n, r, t, l);
r = t;
}
}
cout << ans << '\n';
return 0;
}
C - Prepare Another Box
洛谷链接
AtCoder链接
题目
有
高桥想把所有玩具分别存放在不同的盒子里,他决定按以下步骤依次进行:
- 选择一个任意正整数
x 并购买一个大小为x 的盒子。 - 将每个
N 玩具放入N 盒中(现有的N-1 盒加上新买的盒子)。在这里,每个玩具只能放进一个大小不小于该玩具大小的盒子里,任何盒子都不能装两个或两个以上的玩具。
他想通过在步骤
请判断是否存在一个
思路
让我们首先考虑一下,当我们确定新购买的箱子的大小为
总之,可以使用下面的命题:
将
A 按升序排序,得到a=(a_1,a_2, \ldots ,a_N) 。将x 插入B ,并按升序排序,得到b=(b_1,b_2, \ldots ,b_N) 。那么当且仅当a_i \le b_i 代表所有的i(1 \le i \le N) 时,步骤2 才是可行的。
证明:
- 充分性显而易见,因此我们将证明必要性:“如果第
2 步可行,则所有i(1 \le i \le N) 均为a_i \le b_i 。” - 将玩具和盒子重新编号,这样
a_i 对应的玩具称为玩具i ,b_i 对应的盒子称为盒子i 。当第2 步可行时,存在一个(1,2 \ldots ,N) 的排列p=(p_1,p_2, \ldots ,p_N) ,使得“所有i(1 \le i \le N) 都是a_i \le b_{p_i} ”。 - 假设存在
i(1 \le i<N) 与p_i>p_{i+1} 。那么我们有a_i \le a_{i+1} \le b_{p_{i+1}} \le b_{p_i} ,因此有a_i \le b_{p_{i+1}} 和a_{i+1} \le b_{p_i} ;那么在交换p_i 和p_{i+1} 之后,仍然成立。当i(1 \le i<N) 与p_i>p_{i+1} 同时存在时,重复这一操作,就可以得到p=(1,2, \ldots ,N) ,同时保持。当p=(1,2, \ldots ,N) 存在时,表示所有i(1 \le i \le N) 的a_i \le b_i ,这正是我们想要的结果。
因此,根据这个条件,我们可以使用二进制搜索找到满足条件的最小值
我们也可以不用二进制搜索,而用下面的贪心算法来解决这个问题。(有效性来自上面对固定
- 假设玩具
i 和盒子j 分别是最大的剩余玩具和盒子。 - 如果
A_i \le B_j :将玩具i 放入盒子j 是最优选择。从玩具集合中删除玩具i ,从盒子集合中删除盒子j ;返回第1 步。 - 如果
A_i>B_j :你无法将玩具i 放入任何一个剩余的盒子中,因此你需要购买一个大小至少为A_i 的新盒子。购买一个大小超过A_i 的盒子是没有用的,因此设置x=A_i 来应用上述标准。如果符合,则答案为A_i ;否则为-1 。
通过观察这个贪心算法的行为,我们可以得出一个更简单的算法,如下所示。复杂度仍为
- 将
A 和B 按升序排序。 - 如果存在
i(1 \le i \le N-1) 与A_i>B_i :答案为-1 。 - 否则:答案为
A_{i'+1} ,其中i' 是i(1 \le i \le N-1) 与A_{i+1}>B_i 的最大值(或0 ,如果什么都不适用)。
代码
#include <bits/stdc++.h>
using namespace std;
int n;
int main()
{
scanf("%d", &n);
vector<int> a(n), b(n - 1);
for (int &i : a)
scanf("%d", &i);
for (int &i : b)
scanf("%d", &i);
sort(a.begin(), a.end());
sort(b.begin(), b.end());
for (int i = 0; i < n - 1; i ++ )
{
if (a[i] > b[i])
{
puts("-1");
return 0;
}
}
for (int i = n - 2; i >= 0; i -- )
{
if (a[i + 1] > b[i])
{
cout << a[i + 1] << '\n';
return 0;
}
}
cout << a[0] << '\n';
return 0;
}