刷题集:美国节日


牛客链接:美国节日

一、题目介绍

和中国的节日不同,美国的节假日通常是选择某个月的第几个星期几这种形式,因此每一年的放假日期都不相同。具体规则如下:

  • 1月1日:元旦
  • 1月的第三个星期一:马丁·路德·金纪念日
  • 2月的第三个星期一:总统节
  • 5月的最后一个星期一:阵亡将士纪念日
  • 7月4日:美国国庆
  • 9月的第一个星期一:劳动节
  • 11月的第四个星期四:感恩节
  • 12月25日:圣诞节

现在给出一个年份,请你帮忙生成当年节日的日期。

输入描述:

输入包含多组数据,每组数据包含一个正整数year(2000≤year≤9999)

输出描述:

对应每一组数据,以“YYYY-MM-DD”格式输出当年所有的节日日期,每个日期占一行。
每组数据之后输出一个空行作为分隔。

二、思路概况

题目中一共有两种类型的节日:固定日期的节日和非固定日期的节日

(1)对于前者直接输出即可

(2)对于后者我们就需要找到一个基准时间来求某月的第几个星期几,这里的基准时间设置为该月的1号

比如今天是2022年3月1日,想要求2022年3月的第三个星期一是几号,就可以根据3月1日是星期几,从而进行推断3月的第三个星期一是几号

那么,现在要求的问题就是2022年3月1日是星期几,如果我明确知晓2022年2月21日是星期一,那么根据2月21日和3月1日相差几天,结果%上一个7,就可以知道3月1日是星期几了(相差8天,那么1+8%7=2,3月1日是星期二)。在这里为了求出相差几天更加的方便简洁,采用的基准时间为公元前1年12月31日,该日为星期天

再求和公元前1年12月31日相差几天的过程中需要注意闰年要多一天。

三、详细过程

(1)给定年月日(Y-M-D)求和公元前1年12月31日相差几天(days)

总公式:days = (Y-M-D)-(0000-12-31)= 一共过去的完整的年的天数 + 最后一年(第Y年)过去的天数

分公式1:一共过去的完整的年的天数 =(Y-1)* 365 + 这些完整的年中闰年数 * 1

分公式2:闰年数 = (Y - 1)/ 4 - (Y - 1) / 100 + (Y - 1) / 400

分公式3:最后一年过去的天数 = 经过的完整的月的天数之和+D+是否是闰年?1 : 0(过了2月的前提,否则不用加上最后一项)

分公式4:闰年 = (Y % 400 == 0)||(Y % 4 == 0 && Y % 100 != 0)

在这里插入图片描述

由上面的公式可以推出

days = (Y-M-D)-(0000-12-31)=(Y-1)*365 + (Y-1)/4 - (Y-1)/100 + 
		(Y-1)/400 +  完整月的天数 + D + 是否是闰年?1:0

至此就可以算出相差的天数,(Y-M-D)是星期(days % 7 == 0 ? 7 :days%7

由于days的结果需要 % 7,而 365 等于364 + 1,364 等于 52 * 7,days 公式可以简化为:

days = (Y-1) + (Y-1)/4 - (Y-1)/100 + (Y-1)/400 + 完整月的天数 + D + 是否是闰年?1:0

(2)根据某月1日是星期X(已求出)来知晓当月的第W个星期Q是几号

根据上面的分析我们可以求助某月1日是星期几,比如 1 月1 日已经被求出是星期六,想要求1月的第三个星期一:马丁·路德·金纪念日,我们可以经过找规律来得出结论:

在这里插入图片描述

推出结论:1 号为星期X时,第三个星期一的日期是 1 + 2 * 7 +(7-X+1)% 7

再分析一下11月的第四个星期四:感恩节,已知11月1 日是周二,那么可以通过找规律得到:

在这里插入图片描述

结论:1号为星期X时,第W个星期Q的日期是1 + (W-1) * 7 + (7-X+Q) % 7

所有日期中还有一个比较特殊的,就是5月的最后一个星期一:阵亡将士纪念日

可以将 6 月1 日当做 5 月32日,已知 6 月1日为星期X,那么5月的最后一个星期一可以推出:

在这里插入图片描述

结论:6月1日为星期X时,5月的最后一个星期一的日期是 32 - (X == 1 ?7 : X - 1)

四、代码书写

import java.util.*;
public class Main {
    //来判断是否是闰年
    public static boolean isYear(int Y) {
        return ((Y % 400 == 0)||(Y % 4 == 0 && Y % 100 != 0));
    }
    //来算算第Y年已经过了多少天了
    private static final int[] mouth = {31,28,31,30,31,30,31,31,30,31,30,31};//按照平年的来算
    public static int yDay(int Y,int M,int D) {
        int ret = 0;
        for(int i = 0;i < M-1;i ++) {
            ret += mouth[i];
        }
        //如果月份过了2月就来判断一下是否是闰年,是的话加一
        if(M > 2 && isYear(Y)) {
            ret ++;
        }
        return ret + D;
    }
    //来算算从0000-12-31到Y-M-D一共隔的天数(简化过的结果)
    public static int count(int Y,int M,int D) {
        return (Y - 1) + (Y - 1)/ 4 - (Y - 1) / 100 + (Y - 1) / 400 + yDay(Y,M,D);
    }
    //来算算某年某月的一号是星期几
    public static int One(int Y,int M,int D) {
        int X = count(Y,M,D)%7;
        if(X == 0){
            X = 7;
        }
        return X;
    }
    //来算算第W个星期Q的日期
    public static int m1(int X,int W,int Q) {
        return 1 + (W-1) * 7 + (7-X+Q) % 7;
    }
    //来算算5月的最后一个星期一的日期
    public static int m2(int X) {
        int d = (X == 1? 7:X-1);
        return 32 - d;
    }
    
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while(scan.hasNextInt()) {
            int Y = scan.nextInt();
            System.out.printf("%d-01-01\n",Y);//1月1日
            int X = 0;
            X = One(Y,1,1);
            System.out.printf("%d-01-%02d\n",Y,m1(X,3,1));//1月的第三个星期一
            X = One(Y,2,1);
            System.out.printf("%d-02-%02d\n",Y,m1(X,3,1));//2月的第三个星期一
            X = One(Y,6,1);
            System.out.printf("%d-05-%02d\n",Y,m2(X));//5月的最后一个星期一
            System.out.printf("%d-07-04\n",Y);//7月4日
            X = One(Y,9,1);
            System.out.printf("%d-09-%02d\n",Y,m1(X,1,1));//9月的第一个星期一
            X = One(Y,11,1);
            System.out.printf("%d-11-%02d\n",Y,m1(X,4,4));//11月的第四个星期四
            System.out.printf("%d-12-25\n",Y);
            System.out.println();//12月25日
        }
    }
}

完!

  • 22
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

富春山居_ZYY(已黑化)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值