复赛一:儒略日(Julian)

洛谷:P7075
OJ:CSPS2020A

算法逻辑:

  1. 日期转换原理:
    • 给定儒略日,需要找到对应的公历日期。
    • 由于直接计算日期可能比较复杂,代码采用了 二分查找 的方法,找到累计天数不小于给定儒略日的最小年份。
  2. 闰年计算与历法变更:
    • 儒略历(公元前4713年到1582年10月4日):
      • 闰年规则:每4年一个闰年。
    • 格里高利历(1582年10月15日及之后):
      • 闰年规则:能被4整除但不能被100整除,或者能被400整除的年份为闰年。
      • 历法改革时,1582年10月5日至14日被删除,共10天缺失。
  3. 特殊日期处理:
    • 1582年10月5日至14日的日期缺失:
      • 在计算月份和日期时,需要特别处理这个时间段,确保日期连续性。
      • 在年份为1582年、月份为10月且日期在5日或之后时,日期需要加上10天。
  4. 公元前年份的处理:
    • 由于没有公元0年,公元前1年对应于 year = 0
    • 在输出时,需要将 year 转换为正数,并加上 “BC” 标识。
  5. 二分查找年份:
    定义搜索范围:int left = -4712, right = 1e9 + 5;
    使用二分查找找到满足 calculate_total_days(year) >= julian_day 的最小年份 year
    更新 julian_day,计算在该年份内的天数:julian_day -= calculate_total_days(year - 1);

代码实现:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
/**************************************************************** 
 * Description: 2020提高组复赛第一题 儒略日
 * Author: Alex Li
 * Date: 2024-09-29 17:20:51
 * LastEditTime: 2024-09-29 18:51:10
****************************************************************/
#include <iostream>
using namespace std;
typedef long long LL;

// 每个月的天数数组,初始为平年(非闰年)的天数
int days_in_month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

// 计算从起始年份到指定年份之间的总天数
LL calculate_total_days(LL year) {
    int start_year = -4712; // 起始年份(公元前4713年,因为没有公元0年)
    if (year < start_year) return 0;
    // 按每年365天计算初始总天数
    LL total_days = (year - start_year + 1) * 365;

    if (year < 1582) {
        // 儒略历闰年,每4年一个闰年
        total_days += (year - start_year) / 4 + 1;
    } else {
        // 格里高利历的调整
        total_days -= 10; // 调整1582年10月5日至14日缺失的10天
        // 计算从起始年份到1581年的闰年天数(儒略历规则)
        total_days += (1581 - start_year) / 4 + 1;
        // 计算1582年之后的闰年天数(格里高利历规则)
        total_days += (year - 1580) / 4 - (year - 1500) / 100 + (year - 1200) / 400;
    }
    return total_days;
}

// 判断指定年份是否为闰年
bool is_leap_year(int year) {
    if (year > 1582) {
        // 格里高利历闰年规则:能被4整除且不能被100整除,或能被400整除
        return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    } else {
        // 儒略历闰年规则:能被4整除
        return year % 4 == 0;
    }
}

int main() {
    freopen("julian.in","r",stdin);
    freopen("julian.out","w",stdout);
    int query_count;
    cin >> query_count;
    while (query_count--) {
        LL julian_day;
        cin >> julian_day;
        julian_day++; // 将儒略日调整为从1开始计数

        // 二分查找年份,使得calculate_total_days(year) >= julian_day
        int left = -4712, right = 1e9 + 5;
        while (left < right) {
            int mid = (left + right) >> 1;
            if (calculate_total_days(mid) >= julian_day) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        int year = left; // 找到对应的年份
        julian_day -= calculate_total_days(year - 1); // 计算该年份内的天数

        // 更新当前年份二月的天数,考虑闰年
        days_in_month[2] = 28 + is_leap_year(year);

        // 处理1582年10月的特殊情况(缺少10天)
        days_in_month[10] = (year == 1582) ? 21 : 31;

        int month;
        // 遍历月份,找到对应的月份
        for (int i = 1; i <= 12; i++) {
            if (julian_day > days_in_month[i]) {
                julian_day -= days_in_month[i];
            } else {
                month = i;
                break;
            }
        }

        int day = julian_day; // 剩余的天数即为日期

        // 处理1582年10月5日至14日不存在的日期
        if (year == 1582 && month == 10) {
            if (day >= 5) day += 10; // 跳过缺失的日期
            cout << day << " " << month << " " << year << endl;
        } else {
            if (year > 0)
                cout << day << " " << month << " " << year << endl;
            else
                cout << day << " " << month << " " << -year + 1 << " BC" << endl; // 公元前年份处理
        }
    }
    return 0;
}
Scroll to Top