日期问题总结

做日期问题,首先把模版写下来,再根据具体情况具体分析,基本上考试考到日期问题都是模拟和枚举,数据量也不会太大,所以我们根据题目要求直接打暴力就可以过.

模板:

const int months[]={//平年天数 
	0,31,28,31,30,31,30,31,31,30,31,30,31
};
int is_leap(int y){//判断是否为闰年 
	if(y%100!=0&&y%4==0||y%400==0) return 1;
	return 0;
}
int get_month_days(int y,int m){//判断某个月天数 
	int res=months[m];
	if(m==2) res+=is_leap(y);
	return res;
}
int get_total_days(int y,int m,int d){//计算第一天到某个日期天数 
	int res=0;
	for(int i=1;i<y;i++)//计算前y-1年的天数 
		res+=365+is_leap(i);
	for(int i=1;i<m;i++)//计算第i年前m-1个月的天数 
		res+=get_month_days(y,i);
	return res+d;//第y年第m月的天数 
}

注 :写日期问题时,这三个模板可能不会都用上,也可能都不会用上.

例题:

日期差值

有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天。

输入格式

输入包含多组测试数据。

每组数据占两行,分别表示两个日期,形式为 YYYYMMDD

输出格式

每组数据输出一行,即日期差值。

数据范围

年份范围 [1,9999]
保证输入日期合法。
测试数据的组数不超过 100。

输入样例:
20110412
20110422
输出样例:
11
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<list>
#include<deque>
const int N=5e5+5;
typedef long long LL;
using namespace std;
const int months[]={//平年天数 
	0,31,28,31,30,31,30,31,31,30,31,30,31
};
int is_leap(int y){//判断是否为闰年 
	if(y%100!=0&&y%4==0||y%400==0) return 1;
	return 0;
}
int get_month_days(int y,int m){//判断某个月天数 
	int res=months[m];
	if(m==2) res+=is_leap(y);
	return res;
}
int get_total_days(int y,int m,int d){//计算第一天到某个日期天数 
	int res=0;
	for(int i=1;i<y;i++)//计算前y-1年的天数 
		res+=365+is_leap(i);
	for(int i=1;i<m;i++)//计算第i年前m-1个月的天数 
		res+=get_month_days(y,i);
	return res+d;//第y年第m月的天数 
}
int main()
{
	int y1,m1,d1,y2,m2,d2;
	while(~scanf("%04d%02d%02d\n%04d%02d%02d",&y1,&m1,&d1,&y2,&m2,&d2))
		printf("%d\n",abs(get_total_days(y1,m1,d1)-get_total_days(y2,m2,d2))+1);
    return 0;
}

 

回文日期​​​​​​​

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用 8 位数字表示一个日期,其中,前 4 位代表年份,接下来 2 位代表月份,最后 2 位代表日期。

显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的 8 位数字是回文的。

现在,牛牛想知道:在他指定的两个日期之间(包含这两个日期本身),有多少个真实存在的日期是回文的。

一个 8 位数字是回文的,当且仅当对于所有的 i(1≤i≤8) 从左向右数的第 i 个数字和第 9−i 个数字(即从右向左数的第 i 个数字)是相同的。

例如:

  • 对于 2016 年 11 月 19 日,用 8位数字 20161119 表示,它不是回文的。
  • 对于 2010 年 1 月 2 日,用 8 位数字 20100102 表示,它是回文的。
  • 对于 2010 年 10 月 2 日,用 8 位数字 20101002 表示,它不是回文的。
输入格式

输入包括两行,每行包括一个 8 位数字。

第一行表示牛牛指定的起始日期 date1,第二行表示牛牛指定的终止日期 date2。保证 date1 和 date2 都是真实存在的日期,且年份部分一定为 4 位数字,且首位数字不为 0。

保证 date1 一定不晚于 date2。

输出格式

输出共一行,包含一个整数,表示在 date1 和 date2 之间,有多少个日期是回文的。

输入样例:
20110101
20111231
输出样例:
1

 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<list>
#include<deque>
const int N=5e5+5;
typedef long long LL;
using namespace std;
const int months[]={//平年天数 
	0,31,28,31,30,31,30,31,31,30,31,30,31
};
int is_leap(int y){//判断是否为闰年 
	if(y%100!=0&&y%4==0||y%400==0) return 1;
	return 0;
}
int get_month_days(int y,int m){//判断某个月天数 
	int res=months[m];
	if(m==2) res+=is_leap(y);
	return res;
}
/*int get_total_days(int y,int m,int d){//计算第一天到某个日期天数 
	int res=0;
	for(int i=1;i<y;i++)//计算前y-1年的天数 
		res+=365+is_leap(i);
	for(int i=1;i<m;i++)//计算第i年前m-1个月的天数 
		res+=get_month_days(y,i);
	return res+d;//第y年第m月的天数 
}*/
bool check(int date){
	int year=date/10000;
	int month=date%10000/100;
	int day=date%100;
	if(month<1||month>12) return false;
	if(day<1||day>get_month_days(year,month)) return false;
	return true;
}
int main()
{
	int date1,date2;
	cin>>date1>>date2;
	int res=0;
	for(int i=1000;i<9999;i++){
		string a=to_string(i);
		string b(a.rbegin(),a.rend());
		int date=stoi(a+b);
		if(date>=date1&&date<=date2&&check(date))
			res++;
	}
	cout<<res<<endl;
    return 0;
}

日期问题​​​​​​​

小明正在整理一批历史文献。这些历史文献中出现了很多日期。

小明知道这些日期都在1960年1月1日至2059年12月31日。

令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。

更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。

比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。

给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

输入格式

一个日期,格式是”AA/BB/CC”。

即每个’/’隔开的部分由两个 0-9 之间的数字(不一定相同)组成。

输出格式

输出若干个不相同的日期,每个日期一行,格式是”yyyy-MM-dd”。

多个日期按从早到晚排列。

数据范围

0≤A,B,C≤9

输入样例:
02/03/04
输出样例:
2002-03-04
2004-02-03
2004-03-02
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<list>
#include<deque>
const int N=5e5+5;
typedef long long LL;
using namespace std;
/*const int months[]={//平年天数 
	0,31,28,31,30,31,30,31,31,30,31,30,31
};
int is_leap(int y){//判断是否为闰年 
	if(y%100!=0&&y%4==0||y%400==0) return 1;
	return 0;
}
int get_month_days(int y,int m){//判断某个月天数 
	int res=months[m];
	if(m==2) res+=is_leap(y);
	return res;
}
int get_total_days(int y,int m,int d){//计算第一天到某个日期天数 
	int res=0;
	for(int i=1;i<y;i++)//计算前y-1年的天数 
		res+=365+is_leap(i);
	for(int i=1;i<m;i++)//计算第i年前m-1个月的天数 
		res+=get_month_days(y,i);
	return res+d;//第y年第m月的天数 
}*/
int a,b,c;
int check(int i,int j,int k)
{
	i=i%100;
	if(a==i&&b==j&&c==k) return 1;
	if(a==j&&b==k&&c==i) return 1;
	if(a==k&&b==j&&c==i) return 1;
	return 0;
}
int main()
{
	scanf("%d/%d/%d",&a,&b,&c);
	int month[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
	for(int i=1960;i<=2059;i++){
		if((i%4==0&&i%100!=0)||i%400==0){
			month[2]=29;
		}else{
			month[2]=28;
		}
		for(int j=1;j<=12;j++){
			for(int k=1;k<=month[j];k++){
				if(check(i,j,k)){
					printf("%d-%02d-%02d\n",i,j,k);
				}
			}
		}
	}
    return 0;
}

 

回文日期​​​​​​​

2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2 日。

因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202,恰好是一个回文数。

我们称这样的日期是回文日期。

有人表示 20200202 是“千年一遇” 的特殊日子。

对此小明很不认同,因为不到 22 年之后就是下一个回文日期:20211202 即 2021 年 12 月 2 日。

也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA 型的回文日期。

对此小明也不认同,因为大约 100 年后就能遇到下一个 ABABBABA 型的回文日期:21211212 即 2121 年 12 月 12 日。

算不上“千年一遇”,顶多算“千年两遇”。

给定一个 8 位数的日期,请你计算该日期之后下一个回文日期和下一个 ABABBABA 型的回文日期各是哪一天。

注意: 本题数据保证一定有解。

注意

下一个回文日期和下一个 ABABBABA 型的回文日期可能是同一天。

ABABBABA 型的回文日期,需要满足 A≠B。

输入格式

输入包含一个八位整数 N,表示日期。

输出格式

输出两行,每行 1 个八位数。

第一行表示下一个回文日期,第二行表示下一个 ABABBABA 型的回文日期。

数据范围

对于所有评测用例,10000101≤N≤89991231,保证 N 是一个合法日期的 8 位数表示。

输入样例:
20200202
输出样例:
20211202
21211212

 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<list>
#include<deque>
const int N=5e5+5;
typedef long long LL;
using namespace std;
const int months[]={//平年天数 
	0,31,28,31,30,31,30,31,31,30,31,30,31
};
/*int is_leap(int y){//判断是否为闰年 
	if(y%100!=0&&y%4==0||y%400==0) return 1;
	return 0;
}
int get_month_days(int y,int m){//判断某个月天数 
	int res=months[m];
	if(m==2) res+=is_leap(y);
	return res;
}
int get_total_days(int y,int m,int d){//计算第一天到某个日期天数 
	int res=0;
	for(int i=1;i<y;i++)//计算前y-1年的天数 
		res+=365+is_leap(i);
	for(int i=1;i<m;i++)//计算第i年前m-1个月的天数 
		res+=get_month_days(y,i);
	return res+d;//第y年第m月的天数 
}*/
bool check(int date)
{
    int year = date / 10000;
    int month = date % 10000 / 100;
    int day = date % 100;
    if(!day || month < 0 || month > 12 ) return false;
    if(month != 2 && day >months[month]) return false;
    if(month == 2)
    {
        if((year%4==0&&year%100!=0)||(year%400==0)) //闰年特判
        {
            if(day > 29) return false;
        }
        else 
        {
            if(day > 28) return false;
        }
    }
    return true;
}
bool check1(string s)//判断是否是回文日期
{
    int len = s.size();
    for(int i = 0, j = len - 1; i < j ; i++,j--)//双指针
    {
        if(s[i] != s[j]) return false;
    }
    return true;
}
bool check2(string s)//判断是否是ABABBABA型的回文日期
{
    if(check1(s))//首先该日期要满足回文格式
    {
       if(s[0]!=s[2] || s[1]!= s[3] || s[0] == s[1]) return false;
       return true;
    }
}
int main()
{
	int date,flag=0;
    cin>>date;
    for(int i = date + 1; ;i++)
    {
        if(check(i))
        {
            string s = to_string(i);
            if(check1(s)&&!flag)//输出回文日期
            {
                cout<<i<<endl;
                flag = 1;//标记一下,避免多次输出
            }
            if(check2(s))//输出ABABBABA 型的回文日期
            {
                cout<<i<<endl;
                return 0;
            }
        }
    }
    return 0;
}

日期计算​​​​​​​

给定一个年份 y 和一个整数 d,问这一年的第 d 天是几月几日?

注意闰年的 2 月有 29 天。

满足下面条件之一的是闰年:

  1. 年份是 4 的整数倍,而且不是 100 的整数倍;
  2. 年份是 400 的整数倍。
输入格式

输入的第一行包含一个整数 y,表示年份,年份在 1900 到 2015 之间(包含 1900 和 2015)。

输入的第二行包含一个整数 d,d 在 1 至 365 之间。

输出格式

输出两行,每行一个整数,分别表示答案的月份和日期。

数据范围

1900≤y≤2015,
1≤d≤365

输入样例1:
2015
80
输出样例1:
3
21
输入样例2:
2000
40
输出样例2:
2
9

 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<list>
#include<deque>
const int N=5e5+5;
typedef long long LL;
using namespace std;
/*const int months[]={//平年天数 
	0,31,28,31,30,31,30,31,31,30,31,30,31
};
int is_leap(int y){//判断是否为闰年 
	if(y%100!=0&&y%4==0||y%400==0) return 1;
	return 0;
}
int get_month_days(int y,int m){//判断某个月天数 
	int res=months[m];
	if(m==2) res+=is_leap(y);
	return res;
}
int get_total_days(int y,int m,int d){//计算第一天到某个日期天数 
	int res=0;
	for(int i=1;i<y;i++)//计算前y-1年的天数 
		res+=365+is_leap(i);
	for(int i=1;i<m;i++)//计算第i年前m-1个月的天数 
		res+=get_month_days(y,i);
	return res+d;//第y年第m月的天数 
}*/
int d[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int y,m;
int main()
{
	cin>>y>>m;
	if(y%4==0&&y%100!=0||y%400==0) d[2]=29;
	else d[2]=28;
	int x=0;
	while(m>0){
		if(m-d[x]<=0) break;
		else{
			m-=d[x];
			x++;
		}
	}
	cout<<x<<endl<<m;
    return 0;
}

<think>好的,用户想了解C++蓝桥杯竞赛中的日期处理问题及解决方案。首先,我需要回忆一下蓝桥杯常见的日期类题目类型。比如回文日期、闰年判断、星期计算、两个日期之间的间隔天数等等。这些题目通常需要处理日期的合法性、闰年判断以及日期计算。 根据用户提供的引用,特别是引用[1],里面提到了多个日期问题,比如回文日期日期识别、日期类、打印日期等。这说明蓝桥杯日期问题涵盖多个方面,可能需要不同的解决方法。 首先,处理日期问题的基本步骤通常是确定闰年,然后计算每个月的天数,接着处理日期的累加或差值。比如计算n天后的日期,或者两个日期之间的天数差。这里的关键点是如何高效地进行这些计算,避免超时。 然后,用户提到的引用[3]中有关C++的__gcd函数,这可能与日期问题中的周期计算有关,比如求两个周期的最小公倍数,但不确定是否直接相关。不过,日期问题中更多的是算术运算而不是最大公约数。 接下来,我需要整理常见的解决方法。比如,可以将日期转换为从某个基准日(如0000年1月1日)开始的总天数,这样计算差值就变得容易。另外,处理闰年时需要注意2月份的天数变化,闰年的判断规则是能被4整除但不能被100整除,或者能被400整除。 然后,考虑具体例题。例如,回文日期需要生成或识别符合特定格式的日期,如20200202这样的对称结构。这类问题可能需要遍历可能的日期,并检查其是否符合回文条件,同时确保日期的合法性。 另外,日期识别问题可能涉及将字符串或数字分解为年、月、日,并进行合法性校验。例如,输入一个六位数,分解为年月日,并判断是否有效。 对于代码实现,常用的方法包括使用结构体或类来表示日期,编写函数来检查闰年,计算某个月的天数,以及实现日期的加减运算。还可以使用蔡勒公式来计算某一天是星期几,这在某些问题中可能有用。 用户提供的引用[1]中的示例代码可能包括这些处理步骤,比如在“日期计算”中提到的模板法和思维法,可能涉及不同的实现策略。例如,模板法可能是指预先计算每个月的天数数组,然后逐月处理;而思维法可能更偏向数学计算,如将总天数分解为年、月、日。 还需要注意蓝桥杯竞赛中的时间限制,因此代码的效率很重要。例如,在遍历日期时,避免使用过于暴力的方法,而应尽可能优化循环次数。例如,处理回文日期时,可以生成可能的回文格式,然后检查是否合法,而不是遍历所有日期再判断是否为回文。 最后,总结常见的问题类型和对应的解决策略,帮助用户系统化地应对蓝桥杯中的日期处理题目。</think>### C++蓝桥杯日期处理问题及解决方案 #### 1. 基本问题类型 蓝桥杯日期问题常涉及以下场景: - **日期合法性判断**:验证输入的年月日是否有效[^1]。 - **闰年判断**:闰年规则为能被4整除但不能被100整除,或能被400整除[^1]。 - **日期差值计算**:计算两个日期之间的间隔天数[^1]。 - **回文日期**:识别形如`yyyymmdd`的对称日期(如`20200202`)。 - **星期计算**:求某一天对应的星期几(可用蔡勒公式)。 #### 2. 核心代码模板 ##### (1) 日期合法性检查 ```cpp bool isLeap(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } int daysOfMonth[] = {0, 31,28,31,30,31,30,31,31,30,31,30,31}; bool check(int y, int m, int d) { if (m < 1 || m > 12 || d < 1) return false; int max_day = daysOfMonth[m]; if (m == 2 && isLeap(y)) max_day++; return d <= max_day; } ``` ##### (2) 计算某日期是星期几(蔡勒公式) $$ w = \left( y + \left\lfloor \frac{y}{4} \right\rfloor + \left\lfloor \frac{c}{4} \right\rfloor - 2c + \left\lfloor \frac{13(m+1)}{5} \right\rfloor + d + 77 \right) \mod 7 $$ ```cpp int zeller(int y, int m, int d) { if (m < 3) m += 12, y--; int c = y / 100; y %= 100; int w = (y + y/4 + c/4 - 2*c + 13*(m+1)/5 + d + 77) % 7; return (w + 7) % 7; // 返回0表示星期日,1-6对应星期一至星期六 } ``` ##### (3) 计算日期差值(转换为总天数) ```cpp int getDays(int y, int m, int d) { int days = 0; for (int i = 1; i < y; i++) days += isLeap(i) ? 366 : 365; for (int i = 1; i < m; i++) { days += daysOfMonth[i]; if (i == 2 && isLeap(y)) days++; } days += d - 1; return days; } int diffDays(int y1, int m1, int d1, int y2, int m2, int d2) { return abs(getDays(y1,m1,d1) - getDays(y2,m2,d2)); } ``` #### 3. 典型例题分析 **题目**:给定一个8位日期(如`20200202`),判断是否是回文日期,并找出下一个回文日期。 **解决思路**: 1. **提取年月日**:分割字符串为`yyyy`、`mm`、`dd`。 2. **日期合法性**:调用`check(y, m, d)`。 3. **生成回文候选**:直接构造回文格式(如年份`2020`生成`2020|02|02`)。 **代码片段**: ```cpp string nextPalindromeDate(string s) { int year = stoi(s.substr(0,4)); for (int y = year; ; y++) { string s_y = to_string(y); string candidate = s_y + string(s_y.rbegin(), s_y.rend()); int m = stoi(candidate.substr(4,2)); int d = stoi(candidate.substr(6,2)); if (check(y, m, d)) return candidate; } } ``` #### 4. 优化技巧 - **预处理月份天数表**:使用数组存储非闰年各月天数,遇到闰年动态调整2月天数。 - **避免暴力枚举**:对回文日期问题,反向生成候选日期(如取年份生成完整日期)而非遍历所有日期。 - **数学公式替代循环**:如用蔡勒公式替代逐天累加计算星期。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值