链接: https://codeforces.com/contest/1901
目录
A. Line Trip(思维)
题意:
给定一长度为 x x x 的数轴,和 n n n 个加油站,问开车支持来回所需的最小油箱容量。
思路:
维护一个最大值,代表每次刚好到加油站的最小容量,请注意最后的加油站到终点可能有段距离。
代码:
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
ll n, k, ans = 0;
void solve()
{
cin >> n >> k;
vector<ll> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
ans = a[1];
for (int i = 2; i <= n; i++) {
ans = max(ans, a[i] - a[i - 1]);
}
ans = max((k - a[n]) * 2, ans);
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
// cout.tie(0);
ll _;cin>>_;
while(_--)
solve();
return 0;
}
B. Chip and Ribbon(思维/贪心)
题意:
给定一条有初始值的丝带和初始位置任意选择的细胞,你可以进行两个操作:1、将细胞位置从 a i a_i ai 移动到 a i + 1 a_{i+1} ai+1 。2、将细胞传送到任意 a i a_i ai 位置。每次移动会使得 a i a_i ai 位置上的初始值 − 1 -1 −1 问让初始值全变为 0 0 0 的最小传送数。
思路:
我们要尽可能用移动的操作去消除初始值,所以肯定是从左边开始考虑最好,每碰到一个大于上一个的值,肯定要多传送 a i − a i − 1 a_i-a_{i-1} ai−ai−1 遍。具体看代码,要注意一开始放置不消耗传送次数。
代码:
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
ll n, ans = 0;
void solve()
{
cin >> n;
vector<ll> c(n + 1);
for (int i = 1; i <= n; i++) {
cin >> c[i];
}
ans = c[1] - 1;
for (int i = 2; i <= n; i++) {
if (c[i] > c[i - 1] && c[i] != 0) {
ans += c[i] - c[i - 1];
}
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
// cout.tie(0);
ll _;cin>>_;
while(_--)
solve();
return 0;
}
C. Add, Divide and Floor(思维/构造/贪心)
题意:
给定长度为 n n n 的数组 a a a ,每次选定一个 x x x 让得每一个 a i a_i ai 变成 f l o o r ( ( a i + x ) / 2 ) , f l o o r floor((a_i+x)/2),floor floor((ai+x)/2),floor 表示向下取整,若干次操作后使得数组 a a a 内所有值相等。问该操作次数的最小值是多少,如果操作次数大于 n n n 则不用输出每次选择的 x x x ,否则需要。
思路:
我们发现这个操作次数最小会让最大值最快接近最小值,当这两个值相等,其他值也会随之相等。所以只需要维护最大值 a m a x a_{max} amax 和最小值 a m i n a_{min} amin 。容易发现有两种情况:1、 a m a x 、 a m i n a_{max}、a_{min} amax、amin 都会更新,最后相等。2、 a m a x a_{max} amax 更新, a m i n a_{min} amin 不变,最后相等。在这里我选择了 2 2 2 。所以每次选定的 x x x 都是 a m i n a_{min} amin 。
代码:
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
ll n, ans = 0;
void solve()
{
cin >> n;
ll a[n + 1];
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
if (n == 1) {
cout << 0 << endl;
return;
}
sort(a + 1, a + 1 + n);
ans = 0;
vector<ll> ans_a;
while (a[1] != a[n]) {
a[n] = (a[n] + a[1]) / 2;
ans_a.push_back(a[1]);
}
ans = (ll)ans_a.size();
cout << ans << endl;
if (ans > n)
return;
for (int i = 1; i <= ans; i++) {
cout << ans_a[i - 1] << " \n"[i == ans];
}
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
// cout.tie(0);
ll _;cin>>_;
while(_--)
solve();
return 0;
}
D. Yet Another Monster Fight(思维/贪心/DP/前缀和/二分答案)
题意:
给定 n n n 只怪,你可以任意选定一个怪开始攻击,攻击会连带触发,攻击伤害会向相邻的怪随机选择,攻击伤害每次递减 1 1 1 。问这个攻击伤害 x x x 最小为多少,可以在随机情况下使得怪全部寄。
思路(前缀和):
随机情况说明要往最坏情况去考虑,假设我们选定 k k k 位置开始,对于每个位置 i i i ,最坏情况都是先干掉相邻的其他怪再轮到自己,所以当 i ≤ k , x − ( n − i + 1 ) + 1 ≥ a i i≤k,x-(n-i+1)+1≥a_i i≤k,x−(n−i+1)+1≥ai 即 x ≥ a i − i + n x≥a_i-i+n x≥ai−i+n ,当 i ≥ k , x − i + 1 ≥ a i i≥k,x-i+1≥a_i i≥k,x−i+1≥ai 即 x ≥ a i + i + 1 x≥a_i+i+1 x≥ai+i+1 。再用一个前缀和数组和后缀和数组去维护即可。
代码(前缀和):
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll INF_ = 0x3f3f3f3f3f3f3f3f;
ll n, ans = 0;
ll a[N], pre[N], suf[N];
void solve()
{
cin >> n;
ans = INF_;
pre[0] = suf[n + 1] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
pre[i] = max(pre[i - 1], a[i] - i + n);
}
for (int i = n; i >= 1; i--) {
suf[i] = max(suf[i + 1], a[i] + i - 1);
ans = min(ans, max({ pre[i - 1], suf[i + 1], a[i] }));
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
// cout.tie(0);
// ll _;cin>>_;
// while(_--)
solve();
return 0;
}