题解 | #2022牛客OI赛前集训营-普及组第二场题解#
隔离
https://ac.nowcoder.com/acm/contest/40640/A
T1:隔离
鸡尾酒要从 A 地去 B 地办 件事,其中第 件事耗时 分钟,办完之后回到 A 地。但是如果他在 B 地连续待的时间大于等于 分钟,那么行程卡中就会显示他去过 B 地。根据 A 地的防控政策,如果去过 B 地,那么就会被隔离 天( 分钟),隔离之后才能继续正常行动(例如再启程去 B 地,或者在 A 地开始正常生活)。于是他有一个对策,即在 分钟快到的时候就从 B 地回到 A 地,然后再去 B 地,这样 分钟就会重新计时,从 A 地往返一趟 B 地耗时 分钟。现在他在 A 地准备出发,想要在 B 地办完所有事,回 A 地开始正常生活,办 件事的顺序无法打乱,且办每一件事的过程中无法打断。请问他至少需要多少分钟?
我对这道题的题意真是吐血,题目明确指出:
于是他有一个对策,即在 分钟快到的时候就从 B 地回到 A 地,然后再去 B 地,这样 分钟就会重新计时。
所以我以为就按照这个计策来就可以了(快到 分钟的时候回去),没想到可以不使用这个对策,居然还有一次性办完隔离一次的想法!吐了。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n, flag, ans, sum[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> sum[i];
if (sum[i] >= 240) flag = 1; // 必须隔离了
sum[i] += sum[i - 1]; // 前缀
}
if (flag == 0) { // 如果说可以不隔离
int last = 0;
for (int i = 1; i <= n; i++) {
if (sum[i] - sum[last] >= 240) { // 快到240分钟的时候回去
last = i - 1;
ans += 400;
}
}
ans = min(ans, 10080); // 但是这种计策可能还没有直接隔离快
}
else ans = 10080; // 只能隔离
ans += sum[n] + 400;
cout << ans << endl;
}
T2: 和积
给定三个正整数 ,对于一个正整数 ,云浅认为它是「好数」当且仅当
- ;
- 在十进制下的所有位上的数字和为 。
请你求出所有「好数」中,十进制下所有位上数字的积最大的那个。
你需要求出这个数并输出其十进制下所有位上数字的积。如果有多解,选尽可能小的 。
我不是很懂其他的做法,我的做法简洁粗暴:既然直接枚举过不了,观察数据范围 ,有 位数。然而已经给定了数字总和 ,那么直接枚举前 位数,算出个位即可。
时间复杂度 ,稳稳的过。(不用枚举个位少了一个数量级!)
#include <bits/stdc++.h>
using namespace std;
int t, m, n, k;
int main() {
cin >> t;
while (t--) {
int minn = -2e9, id = 0;
cin >> m >> n >> k;
for (int a = 0; a <= 5; a++) { // 暴力枚举前6位,注意第一位最多为5
for (int b = 0; b <= 9; b++) {
for (int c = 0; c <= 9; c++) {
for (int d = 0; d <= 9; d++) {
for (int e = 0; e <= 9; e++) {
for (int f = 0; f <= 9; f++) {
int g = k - a - b - c - d - e - f; // g是个位
if (g > 9 || g < 0) continue;
int t = a * 1e6 + b * 1e5 + c * 1e4 + d * 1e3 + e * 1e2 + f * 10 + g; // 算出现在凑出来的数字
if (t < m || t > n) continue; // 合法
int mul = 0; // 算乘积
if (a == 0 && b == 0 && c == 0 && d == 0 && e == 0 && f == 0 && g == 0) mul = 0; // 注意需要特殊判断前导0
else if (a == 0 && b == 0 && c == 0 && d == 0 && e == 0 && f == 0) mul = g; // 前导0
else if (a == 0 && b == 0 && c == 0 && d == 0 && e == 0) mul = f * g; // 前导0
else if (a == 0 && b == 0 && c == 0 && d == 0) mul = e * f * g; // 前导0
else if (a == 0 && b == 0 && c == 0) mul = d * e * f * g; // 前导0
else if (a == 0 && b == 0) mul = c * d * e * f * g; // 前导0
else if (a == 0) mul = b * c * d * e * f * g; // 前导0
else mul = a * b * c * d * e * f * g; // 前导0
if (mul > minn) minn = mul, id = t; // 最后统计答案
}
}
}
}
}
}
cout << id << ' ' << minn << endl;
}
}
T3: 电梯停靠
推理后的题意:给定 个双元组 ,求出整数 使得 。
根据小学数学的"奇数中间点,偶数中间段"的思想,可以快速求出答案。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
int n, m, a[N], b[N], t[N], l, res;
signed main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
scanf("%lld%lld", &a[i], &b[i]);
t[++l] = a[i], t[++l] = b[i];
}
sort(t + 1, t + l + 1);
cout << t[l / 2] << ' '; // 取最中间的那个点
for (int i = 1; i <= m; i++) {
res += abs(t[l / 2] - a[i]) + abs(t[l / 2] - b[i]) + abs(a[i] - b[i]);
}
cout << res << endl;
}