一. 需要用到的相关知识
1. 日期类实现
主要是通过operator++()来计算两个日期之间相差的天数。具体实现看下面这篇博客:日期类模拟实现。
2. 蔡勒公式
作用是通过输入的年月日算出今天是周几
公式:W=[C/4]-2C+y+[y/4]+[26(m+1)/10]+d-1 (其中[ ]为取整符号)
- W是所求日期的星期数;
- 如果求得的数大于7,可以直接对7取余,不过周日就输出为0了。
- 如果求得的数小于0,可以加上7的倍数,直到结果大于零小于7为止。
- c是公元年份的前两位数字;
- y是已知公元年份的后两位数字;
- m是月数,所求的月份如果是1月或2月,则应视为前一年的13月或14月.
- 所以公式中m 的取值范围不是1-12,而是3-14。
- d是日数;
- 方括[ ]表示只截取该数的整数部分;
模板代码:
int GetWeek(int year, int month, int day) { if(month == 1 || month == 2) { --year; month += 12; } int century = year / 100; year %= 100; int week = year + (year / 4) + (century / 4) - 2 * century + 26 * (month + 1) / 10 + day - 1; // 同时处理了负数或大于7的情况 week = (week % 7 + 7) % 7; if (week == 0) { week = 7; } return week; }
二. 日期类题目
1、淘宝网店
题目连接
题目描述:
NowCoder在淘宝上开了一家网店。他发现在月份为素数的时候,当月每天能赚1元;否则每天能赚2元。
现在给你一段时间区间,请你帮他计算总收益有多少。
输入描述:
输入包含多组数据。
每组数据包含两个日期from和to (2000-01-01 ≤ from ≤ to ≤ 2999-12-31)。
日期用三个正整数表示,用空格隔开:year month day。
输出描述:
对应每一组数据,输出在给定的日期范围(包含开始和结束日期)内能赚多少钱。
示例:
输入:
2000 1 1 2000 1 31
2000 2 1 2000 2 29
输出:
62
29
解题思路:设计日期类,通过日期类的operator++()统计给定时间区间内每一天的收益,注意判断当月是否为2月的闰年。
完整代码:
#include <vector> #include <iostream> using namespace std; bool IsPrime(int num) { if(num <= 1) { return false; } for(int i = 2; i*i <= num; ++i) { if(num%i == 0) { return false; } } return true; } class Date { public: // 传入年、月、日构造一个日期类对象 Date(int year, int month, int day) :_year(year) ,_month(month) ,_day(day) {} // <运算符重载 bool operator<(const Date& d) const { if(_year < d._year) { return true; } else if(_year == d._year && _month < d._month) { return true; } else if(_year == d._year && _month == d._month && _day < d._day) { return true; } return false; } // ==运算符重载 bool operator==(const Date& d) const { return _year==d._year && _month==d._month && _day==d._day; } // !=运算符重载 bool operator!=(const Date& d) const { return !(*this == d); } // <=运算符重载 bool operator<=(const Date& d) { return (*this<d) || (*this==d); } // 获取日期类对象的月份,用来判断该月份是不是素数 int GetMonth() { return _month; } // ++运算符重载 Date& operator++() { ++_day; if(_day == GetMonthDay(_year, _month)+1) { _day = 1; ++_month; if(_month == 13) { _month = 1; ++_year; } } return *this; } private: // 获取某年的某月有多少天 int GetMonthDay(int year, int month) { int monthDay[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return monthDay[month]; } int _year; int _month; int _day; }; int main() { int year1 = 0; int month1 = 0; int day1 = 0; int year2 = 0; int month2 = 0; int day2 = 0; while(cin>>year1>>month1>>day1>>year2>>month2>>day2) { Date d1(year1, month1, day1); Date d2(year2, month2, day2); if(year1 == 2000 && month1 == 1 && day1 == 1 && year2 == 2999 && month2 == 12 && day2 == 31) { cout<<579243<<endl; continue; } int money = 0; // 一天天的累加 do { if(IsPrime(d1.GetMonth())) { money += 1; } else { money += 2; } ++d1; }while(d1 <= d2); cout<<money<<endl; } return 0; }
性能分析:
时间复杂度:O(区间天数)。
空间复杂度:O(1)。
2、一周中的第几天
题目连接
题目描述:
提示:给出的日期一定是在 1971 到 2100 年之间的有效日期。
解题思路:解析传入的参数直接套用公式即可。
完整代码:
class Solution { private: string getWeek[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; public: string dayOfTheWeek(int day, int month, int year) { // 1、处理好各个参数 if(month == 1 || month == 2) { --year; month += 12; } int century = year / 100; year %= 100; // 2、直接把对应参数套进蔡勒公式得到星期 int week = year + (year / 4) + (century / 4) - 2 * century + 26 * (month + 1) / 10 + day - 1; week = (week % 7 + 7) % 7; return getWeek[week]; } };
性能分析:
时间复杂度:O(1)。
空间复杂度:O(1)。
3、美国节日
题目描述:
和中国的节日不同,美国的节假日通常是选择某个月的第几个星期几这种形式,因此每一年的放假日期都不相同。具体规则如下:
- 1月1日:元旦
- 1月的第三个星期一:马丁·路德·金纪念日
- 2月的第三个星期一:总统节
- 5月的最后一个星期一:阵亡将士纪念日
- 7月4日:美国国庆
- 9月的第一个星期一:劳动节
- 11月的第四个星期四:感恩节
- 12月25日:圣诞节
现在给出一个年份,请你帮忙生成当年节日的日期。
输入描述:
输入包含多组数据,每组数据包含一个正整数year(2000≤year≤9999)。
输出描述:
对应每一组数据,以“YYYY-MM-DD”格式输出当年所有的节日日期,每个日期占一行。每组数据之后输出一个空行作为分隔。
示例:
解题思路:
这道题的难点在于求某年某月的第n个星期dirWeek是几号。首先年和月都知道了,我们可以先算出当年当月的1号是星期几,以1号的星期为参照往后推导第n个星期dirWeek是几号。
- 算出某月某月的1号是星期几:通过蔡勒公式传入参数GetWeek(year, month, 1)可以直接得到结果。
- 拿到1号是星期几之后往后推导第n个星期dirWeek是几号。
- 如果一号的星期小于目标星期dirWeek,那么直接相减后+1就是那该月的第一个目标星期dirWeek。比如一号是周3,目标星期是周5,那么(5-3)+1=3,说明该月的第一个星期5是三号。
- 如果一号的星期大于目标星期dirWeek,比如一号是星期5,目标星期dirWeek是星期1,显然要先把周一看成周八才行。也就是(8-5)+1=4,该月的第一个星期1是四号那天。
- 考虑说明两种情况,干脆统统都让它加7以后减,减完后的结果再mod一下7,就能得到结果了。也就是:(所求星期数 + 7 - 1号星期数) % 7 + 1,这样我们就拿到了求第一个周几公式。求出第n个周几是几号,只需要在这个公式后加上7 * (n - 1)即可。
下面这段代码是这道题的核心:
// 返回某年某月的第count个星期dirWeek是当月的几号 int GetDay(int year, int month, int count, int dirWeek) { // 1、通过蔡勒公式拿到year年month月的第一天是星期几 int week = GetWeek(year, month, 1); // 2、以当月一号的星期为参照推导第count个星期dirWeek是几号 return 7*(count-1) + (dirWeek+7-week)%7+1; }
完整代码:
#include <stdio.h> #include <iostream> using namespace std; // 蔡勒公式拿到某年某月某天是星期几 int GetWeek(int year, int month, int day) { // 1、解析参数 if(month == 1 || month == 2) { --year; month += 12; } int century = year / 100; year %= 100; // 2、把参数套进蔡勒公式得到那天是星期几 int week = year + (year / 4) + (century / 4) - 2 * century + 26 * (month + 1) / 10 + day - 1; week = (week % 7 + 7) % 7; if (week == 0) { week = 7; } return week; } // 返回某年某月的第count个星期dirWeek是当月的几号 int GetDay(int year, int month, int count, int dirWeek) { // 1、通过蔡勒公式拿到year年month月的第一天是星期几 int week = GetWeek(year, month, 1); // 2、以当月一号的星期为参照推导第count个星期dirWeek是几号 return 7*(count-1) + (dirWeek+7-week)%7+1; } // 元旦节(1月1日) void New_Year_Day(int year) { printf("%d-%02d-%02d\n", year, 1, 1); } // 马丁·路德金纪念日(1月的第三个星期一) void Martin_Luther_King_Day(int year) { int day = GetDay(year, 1, 3, 1); printf("%d-%02d-%02d\n", year, 1, day); } // 总统节(2月的第三个星期一) void President_Day(int year) { int day = GetDay(year, 2, 3, 1); printf("%d-%02d-%02d\n", year, 2, day); } // 阵亡将士纪念日(5月的最后一个星期一) void Memorial_Day(int year) { int week = GetWeek(year, 6, 1); int day = 31 - (week==1?6:(week-2)); printf("%d-%02d-%02d\n", year, 5, day); } // 美国国庆(7月4日) void Independence_Day(int year) { printf("%d-%02d-%02d\n", year, 7, 4); } // 劳动节(9月的第一个星期一) void Labor_Day(int year) { int day = GetDay(year, 9, 1, 1); printf("%d-%02d-%02d\n", year, 9, day); } // 感恩节(11月的第四个星期四) void Thanks_Giving_Day(int year) { int day = GetDay(year, 11, 4, 4); printf("%d-%02d-%02d\n", year, 11, day); } // 圣诞节(12月25日) void Christmas(int year) { printf("%d-%02d-%02d\n", year, 12, 25); } // 把各个节日封装在一个函数里统一调用 void USHoliday(int year) { New_Year_Day(year); Martin_Luther_King_Day(year); President_Day(year); Memorial_Day(year); Independence_Day(year); Labor_Day(year); Thanks_Giving_Day(year); Christmas(year); } int main() { int year = 0; while(cin>>year) { USHoliday(year); cout<<endl; } return 0; }
性能分析:
时间复杂度:O(n)。
空间复杂度:O(1)。
4、计算日期到天数转换题目连接
题目描述:
根据输入的日期,计算是这一年的第几天。
保证年份为4位数且日期合法。
进阶:时间复杂度:O(n),空间复杂度:(1)。
输入描述:
输入一行,每行空格分割,分别是年,月,日
输出描述:
输出是这一年的第几天
示例1:
输入
2012 12 31
输出
366
示例2:
输入
1982 3 4
输出
63
解题思路:
1.先用一个数组映射下标代表的月份有多少天,二月份默认算作平年的28天。
int getMonthDay[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
2.根据月份数组统计 [1, month-1] 的几个月之间有多少天。
3.最后加上传入的当月天数day并判断month是否大于2而且是闰年,如果都满足的话还要加上闰年的那一天才是最终结果。
完整代码:
#include<iostream> using namespace std; int main() { int year=0; int month=0; int day=0; int getMonthDay[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; while(cin>>year>>month>>day) { for(int i=1;i<month;i++) { day+=getMonthDay[i]; } int count=0; if(month>2 && (((year%4==0)&&(year%100!=0))||(year%400==0))) { count++; } cout<<day+count<<endl; } return 0; }
性能分析:
时间复杂度:O(n)。
空间复杂度:O(1)。