📜  门|门 CS 1996 |第 46 题(1)

📅  最后修改于: 2023-12-03 14:58:34.775000             🧑  作者: Mango

题目介绍

这是一道来自 1996 年 "门|门" 考试的题目,题目编号为 46。题目背景并不是很明确,但可以看出是由三个子问题组成,比较考验细节问题的处理。

题目描述

题目包含以下三个小问题:

  1. 给定一些时间,格式为 HH:MM,请输出最早和最晚的时间。
  2. 给定一个日期,格式为 YYYY-MM-DD,请输出它是当年的第几天。
  3. 给定一些点的坐标,求它们的最近距离。

解题思路

问题一

问题一其实就是一个找最值的问题。一个比较简单的方法是把所有的时间转换成分钟数,然后找到最小值和最大值再转回时间。具体做法:

  1. 把时间按 "HH:MM" 的格式读入,分别用变量 hourminute 保存小时和分钟数。
  2. 用表达式 minute + hour * 60 把时间转换成分钟数,记为 time
  3. 用变量 min_timemax_time 分别记录最小和最大的 time 值。
  4. min_timemax_time 转换成 "HH:MM" 的格式输出即可。
问题二

问题二更像是一个字符串处理的问题。一个常见的做法是用一个长度为 12 的数组 days 存储每个月的天数,然后把日期字符串分成三个部分,再根据年份判断是否是闰年,从而计算出来这是当年的第几天。具体做法:

  1. 定义一个长度为 12 的整型数组 days,数组下标表示月份,数组元素表示这个月的天数。注意 2 月特殊处理。
  2. strtok() 函数把日期字符串按 '-' 分隔成三个部分。分别用变量 yearmonthday 保存。
  3. 根据年份判断是否是闰年。闰年的规则是:能被 4 整除但不能被 100 整除,或者能被 400 整除。如果是闰年,则将 days[2] 的值设为 29。
  4. 用一个循环计算出这是当年的第几天。具体做法:先统计前几个月的天数,然后加上当前月的天数。
    • 前几个月的天数可以用一个循环来计算。循环变量从 1 到 month-1,每次累加 days[i] 的值。
    • 当前月的天数就是 day 的值。
  5. 把天数输出即可。
问题三

问题三是一个比较经典的计算几何问题,可以使用暴力枚举、分治算法、基于树的算法等多种方法。这里介绍一个暴力枚举的做法。

  1. 枚举两个点 $P_i$ 和 $P_j$,计算它们之间的距离 $d_{i,j}$。
    • 距离公式:$d_{i,j} = \sqrt{(x_i-x_j)^2+(y_i-y_j)^2}$
    • 注意:由于浮点数之间的比较可能存在精度误差,因此应当使用一个很小的正数 $\epsilon$ 来判断两个浮点数是否相等。比如,可以使用表达式 fabs(d1-d2) < epsilon 来判断 d1d2 是否相等。
  2. 维护一个变量 min_dist,初始值设为一个比较大的数,比如 $10^9$。然后遍历所有的点对,找到距离最小的那个,并更新 min_dist 的值。遍历完成后,min_dist 即为所求。
  3. min_dist 输出即可。

解题代码

问题一
#include <iostream>
#include <string>
using namespace std;

int main() {
    int min_time = 24 * 60, max_time = 0;
    string s;
    while (cin >> s) {
        int hour = stoi(s.substr(0, 2));
        int minute = stoi(s.substr(3, 2));
        int time = hour * 60 + minute;
        min_time = min(min_time, time);
        max_time = max(max_time, time);
    }
    printf("%02d:%02d %02d:%02d", min_time / 60, min_time % 60, max_time / 60, max_time % 60);
    return 0;
}
问题二
#include <iostream>
#include <cstring>
using namespace std;

int main() {
    int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    string s;
    cin >> s;
    char *year = strtok(&s[0], "-");
    char *month = strtok(NULL, "-");
    char *day = strtok(NULL, "-");
    int y = atoi(year), m = atoi(month), d = atoi(day);
    if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) days[2] = 29;
    int ans = 0;
    for (int i = 1; i < m; i++) {
        ans += days[i];
    }
    ans += d;
    printf("%d", ans);
    return 0;
}
问题三
#include <iostream>
#include <cmath>
using namespace std;

const int MAXN = 10005;
const double EPS = 1e-8;
int x[MAXN], y[MAXN];

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> x[i] >> y[i];
    }
    double min_dist = 1e9;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j < i; j++) {
            double dx = x[i] - x[j];
            double dy = y[i] - y[j];
            double dist = sqrt(dx * dx + dy * dy);
            if (dist - min_dist < EPS) {
                min_dist = dist;
            }
        }
    }
    printf("%.2lf", min_dist);
    return 0;
}