题解 | #2022牛客OI赛前集训营-普及组第二场题解#

隔离

https://ac.nowcoder.com/acm/contest/40640/A

T1:隔离

鸡尾酒要从 A 地去 B 地办 nn 件事,其中第 ii 件事耗时 aia_i 分钟,办完之后回到 A 地。但是如果他在 B 地连续待的时间大于等于 240240 分钟,那么行程卡中就会显示他去过 B 地。根据 A 地的防控政策,如果去过 B 地,那么就会被隔离 77 天(1008010080 分钟),隔离之后才能继续正常行动(例如再启程去 B 地,或者在 A 地开始正常生活)。于是他有一个对策,即在 240240 分钟快到的时候就从 B 地回到 A 地,然后再去 B 地,这样 240240 分钟就会重新计时,从 A 地往返一趟 B 地耗时 400400 分钟。现在他在 A 地准备出发,想要在 B 地办完所有事,回 A 地开始正常生活,办 nn 件事的顺序无法打乱,且办每一件事的过程中无法打断。请问他至少需要多少分钟?

我对这道题的题意真是吐血,题目明确指出:

于是他有一个对策,即在 240240 分钟快到的时候就从 B 地回到 A 地,然后再去 B 地,这样 240240 分钟就会重新计时。

所以我以为就按照这个计策来就可以了(快到 240240 分钟的时候回去),没想到可以不使用这个对策,居然还有一次性办完隔离一次的想法!吐了。

代码:

#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: 和积

给定三个正整数 M,N,kM,N,k,对于一个正整数 xx,云浅认为它是「好数」当且仅当

  • MxNM\le x\le N
  • xx 在十进制下的所有位上的数字和为 kk

请你求出所有「好数」中,十进制下所有位上数字的积最大的那个。

你需要求出这个数并输出其十进制下所有位上数字的积。如果有多解,选尽可能小的 xx

我不是很懂其他的做法,我的做法简洁粗暴:既然直接枚举过不了,观察数据范围 N,M5×106N,M\leq 5\times 10^6,有 77 位数。然而已经给定了数字总和 kk,那么直接枚举前 66 位数,算出个位即可。

时间复杂度 5×105×T=5×1075\times 10^5\times T=5\times 10^7,稳稳的过。(不用枚举个位少了一个数量级!)

#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: 电梯停靠

推理后的题意:给定 mm 个双元组 (ai,bi)(a_i,b_i),求出整数 x(1xn)x(1\le x\le n) 使得 xai+xbi+aibi\sum |x-a_i|+|x-b_i|+|a_i-b_i|

根据小学数学的"奇数中间点,偶数中间段"的思想,可以快速求出答案。

#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;
}
全部评论
%%%大哥
点赞 回复 分享
发布于 2022-10-07 21:32 江苏

相关推荐

3 收藏 评论
分享
牛客网
牛客企业服务