蓝桥杯——日期问题

214 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情

今天来写一些蓝桥杯的一种常见问题,日期问题是蓝桥杯常见的一种问题,虽然不难,但是我写的代码几乎没有低于100行的qaq,所以我感觉还是有必要整理一下,日期问题一般都是模拟,虽然说代码长,但是呢,很容易发现问题,也是拿分的好题型,就是说有点难ac(bushi)! 话不多说,来看题: 先来看一道普及组的蓝桥杯原题:也是回文日期的一种题型

[NOIP2016 普及组] 回文日期

题目背景

NOIP2016 普及组 T2

题目描述

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

牛牛习惯用 88 位数字表示一个日期,其中,前 44 位代表年份,接下来 22 位代表月份,最后 22 位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的 88 位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。

一个 88 位数字是回文的,当且仅当对于所有的 ii1i81 \le i \le 8)从左向右数的第 ii 个数字和第 9i9-i 个数字(即从右向左数的第 ii 个数字)是相同的。

例如:

  • 对于 2016 年 11 月 19 日,用 88 位数字 2016111920161119 表示,它不是回文的。
  • 对于 2010 年 1 月 2 日,用 88 位数字 2010010220100102 表示,它是回文的。
  • 对于 2010 年 10 月 2 日,用 88 位数字 2010100220101002 表示,它不是回文的。

每一年中都有 1212 个月份:

其中,1,3,5,7,8,10,121, 3, 5, 7, 8, 10, 12 月每个月有 3131 天;4,6,9,114, 6, 9, 11 月每个月有 3030 天;而对于 22 月,闰年时有 2929 天,平年时有 2828 天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

  1. 这个年份是 44 的整数倍,但不是 100100 的整数倍;
  2. 这个年份是 400400 的整数倍。

例如:

  • 以下几个年份都是闰年:2000,2012,20162000, 2012, 2016
  • 以下几个年份是平年:1900,2011,20141900, 2011, 2014

输入格式

两行,每行包括一个 88 位数字。

第一行表示牛牛指定的起始日期。

第二行表示牛牛指定的终止日期。

保证 date1\mathit{date}_1date2\mathit{date}_2 都是真实存在的日期,且年份部分一定为 44 位数字,且首位数字不为 00

保证 date1\mathit{date}_1 一定不晚于 date2\mathit{date}_2

输出格式

一个整数,表示在 date1\mathit{date}_1date2\mathit{date}_2 之间,有多少个日期是回文的。

样例 #1

样例输入 #1

20110101
20111231

样例输出 #1

1

样例 #2

样例输入 #2

20000101
20101231

样例输出 #2

2

提示

【样例说明】

对于样例 1,符合条件的日期是 2011110220111102

对于样例 2,符合条件的日期是 20011002200110022010010220100102

【子任务】

对于 60%60 \% 的数据,满足 date1=date2\mathit{date}_1 = \mathit{date}_2

分析

刚接触这种问题,菜菜视角(我):暴力枚举不就行了?,直接枚举一下两个数,看着感觉是O(1e7)O(1e7)的复杂度,但是貌似其实问题不是这么简单,因为我后来发现,还要把八位数给分解,因此这个复杂度比较危险,所以说(别问我怎么知道的,也不知道是谁试了然后TLE了),后来yy总一语点醒梦中人,虽然是八位数,但是只有99999999年啊,而且最多只有99999999个会问日期啊,只要挨个判断就行了,然后给的那两个八位数,如果对应年份有回文日期且合法,那么我们就可以算进去,否则剪掉。代码实现过程也很简单,主要包括

1.1.判断年份所应该对应的日期是多少 2.2.判断平年闰年然后看前两位数字对应的月和末两位数字对应的天 3.3.特判给的起始八位数和末尾八位数前四位对应的年份是否回文,再看回文日期是否在所给范围之中。

代码

代码还是比较冗长的

#include <iostream>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
bool check(int x){//判断平年闰年
	if(x%4==0 || (x%100!=0 && x%400==0)) return true;
	else return false;
}
int di(int x){//将年份对应的回文月日用四位数表示出来
	int y=0;
	int d[10];
	int cnt=0;
	while(x){
		d[++cnt]=x%10;
		x/=10;
	}
	for(int i=1;i<=4;i++){
		y+=d[i]*pow(10,4-i);
	}
	return y;
}
bool panm(int m){//判断月份是31天还是30天
	if(m==1 || m==3 || m==5 || m==7 || m==8 || m==10 || m==12) return true;
	else return false;
}
int main(){
	int y1,y2;
	cin>>y1>>y2;
	int y3=y1/10000;//取年份
	int y4=y2/10000;//取年份
	int cnt=0;
	for(int i=y3;i<=y4;i++){
		bool ok=0;
		int hz=di(i);
		int mo=hz/100;
		int day=hz%100;
		if(mo>12 || mo<=0) continue;
		else{
			if(mo==1 || mo==3 || mo==5 || mo==7 || mo==8 || mo==10 || mo==12){
				if(day>=1 && day<=31){
					ok=1;
					cnt++;
				}
			}
			else if(mo==2){
				if(check(i)){
					if(day>=1 && day<=29){
						ok==1;
						cnt++;
					}
				}
				else{
					if(day>=1 && day<=28){
						ok==1;
						cnt++;
					}
				}
			}
			else{
				if(day>=1 && day<=30){
					ok=1;
					cnt++;
				}
			}
		}
		if(i==y3 && ok==1){//这里就是看满不满足在所给日期内
			if(di(i)<y1%10000) cnt--;
		}
		if(i==y4 && ok==1){//这里就是看满不满足在所给日期内
			if(di(i)>y2%10000) cnt--;
		}
	}
	cout<<cnt<<endl;
}

[蓝桥杯 2020 省 AB2] 回文日期

题目描述

2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2 日。因为如果将这个日期按 yyyymmdd 的格式写成一个 88 位数是 20200202,恰好是一个回文数。我们称这样的日期是回文日期。

有人表示 20200202 是“千年一遇” 的特殊日子。对此小明很不认同,因为不到 2 年之后就是下一个回文日期:20211202 即 2021 年 12 月 2 日。

也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA 型的回文日期。对此小明也不认同,因为大约 100100 年后就能遇到下一个 ABABBABA 型的回文日期:21211212 即 2121 年12 月12 日。算不上“千年一遇”,顶多算“千年两遇”。

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

输入格式

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

输出格式

输出两行,每行 11 个八位数。第一行表示下一个回文日期,第二行表示下 一个 ABABBABA 型的回文日期。

样例 #1

样例输入 #1

20200202

样例输出 #1

20211202
21211212

提示

对于所有评测用例,10000101N8999123110000101 \le N \le 89991231,保证 NN 是一个合法日期的 88 位数表示。

蓝桥杯 2020 第二轮省赛 A 组 G 题(B 组 G 题)。 由这道题我们引出了蓝桥杯的回文日期,其实就是在输出上变化了一下,这个代码就要在有些地方上变一下,具体如下:由于这题要输出形如abab型的回文日期,于是在我们找到第一个回文日期之和,先要判断这个日期是不是符合abab型的回文日期,如果是我们把当前日期输出两遍就ok了,如果不是,我们再去找下一个就是满足月份等于天数的日期即可。 代码如下:

代码

#include <iostream>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
bool check(int x){
    if(x%4==0 || (x%100!=0 && x%400==0)) return true;
    else return false;
}
int di(int x){
    int y=0;
    int d[10];
    int cnt=0;
    while(x){
        d[++cnt]=x%10;
        x/=10;
    }
    for(int i=1;i<=4;i++){
        y+=d[i]*pow(10,4-i);
    }
    return y;
}
bool panm(int m){
    if(m==1 || m==3 || m==5 || m==7 || m==8 || m==10 || m==12) return true;
    else return false;
}
int main(){
    bool ok1=0,ok2=0;
    int y1;
    cin>>y1;
    int y3=y1/10000;
    int cnt=0;
    for(int i=y3;;i++){
        int rr=i;
        bool ok=0;
        int hz=di(i);
        int mo=hz/100;
        int day=hz%100;
        if(mo>12 || mo<=0) continue;
        else{
            if(mo==1 || mo==3 || mo==5 || mo==7 || mo==8 || mo==10 || mo==12){
                if(day>=1 && day<=31){
                    ok=1;
                }
            }
            else if(mo==2){
                
                if(check(i)){
                    
                    if(day>=1 && day<=29){
                    //    cout<<day<<endl;
                        ok=1;
                    }
                }
                else{
                    if(day>=1 && day<=28){
                        ok=1;
                    }
                }
            }
            else{
                if(day>=1 && day<=30){
                    ok=1;
                }
            }
        }
    //    cout<<day<<endl;
        if(i==y3 && ok==1){
            if(di(i)>=y1%10000){
                if(mo<=9) rr*=10;
                cout<<rr<<di(i)<<endl;
                ok1=1;
            }
        }
        if(i==y3 && ok==1 && mo==day){
            if(di(i)>=y1%10000){
                cout<<rr<<di(i)<<endl;
                break;
            }
        }
        if(i!=y3){
            if(ok==1 && !ok1){
                if(mo<=9) rr*=10;
                cout<<rr<<di(i)<<endl;
                ok1=1;
            }
        }
        if(i!=y3){
            if(ok==1 && day==mo){
                if(mo<=9 && rr<10000) rr*=10;
                cout<<rr<<di(i)<<endl;
                break;
            }
        }
    }
}

接下来这题也是日期问题,但是不是回文日期

[蓝桥杯 2017 省 B] 日期问题

题目描述

小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在 1960 年 1 月 1 日至 2059 年 12 月 31 日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。

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

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

输入格式

一个日期,格式是 AA/BB/CC。(0A,B,C90\le A, B, C\le 9)

输出格式

输出若干个不相同的日期,每个日期一行,格式是 yyyy-MM-dd。多个日期按从早到晚排列。

样例 #1

样例输入 #1

02/03/04

样例输出 #1

2002-03-04  
2004-02-03  
2004-03-02

分析

初次接触这题的时候,我以为这是一个特别难的题,其实这确实是个特别难(模拟)的题,(个人感觉)!不过不同于刚开始,这次我有思路,其实就是一一判断3种情况有没有可能,最后输出,如果可能重复就舍弃!

代码

#include <iostream>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
bool check(int x){
	if(x%400==0 || (x%4==0 && x%100!=0)) return true;
	else return false;
}
struct date{
	int y,m,d;
}day[10];
bool cmp(date aa,date bb){
	if(aa.y==bb.y){
		return aa.m<bb.m; 
		if(aa.m<bb.m){
			return aa.d<bb.d;
		} 
	}
	return aa.y<bb.y;
}
int main(){
	bool ok1=0,ok2=0,ok3=0;
	string s;
	cin>>s;
	int a[10];
	a[1]=s[0]-'0';
	a[2]=s[1]-'0';
	a[3]=s[3]-'0';
	a[4]=s[4]-'0';
	a[5]=s[6]-'0';
	a[6]=s[7]-'0';
	int hz=a[1]*10+a[2];
	int zz=a[3]*10+a[4];
	int dz=a[5]*10+a[6];
	int year;
	if(hz>=0 && hz<=59) year=hz+2000;
	if(hz>=60 && hz<=99) year=hz+1900;
	if(zz>=1 && zz<=12){
		if(zz==2){
			if(check(year)){
				if(dz>=1 && dz<=29){
					ok1=1;
				}
			}
			else{
				if(dz>=1 && dz<=28){
					ok1=1;
				}
			}
		}
		else if(zz==4 || zz==6 || zz==9 || zz==11){
			if(dz>=1 && dz<=30) ok1=1;
		}
		else{
			if(dz>=1 && dz<=31) ok1=1;
		}
	}
	//cout<<year<<"-"<<s[3]<<s[4]<<"-"<<s[6]<<s[7]<<endl;
	if(ok1==1){
		day[1].y=year,day[1].m=zz,day[1].d=dz;
	}
	hz=a[5]*10+a[6];
	zz=a[1]*10+a[2];
	//cout<<zz<<endl;
	dz=a[3]*10+a[4];
	if(hz>=0 && hz<=59) year=hz+2000;
	if(hz>=60 && hz<=99) year=hz+1900;
	if(zz>=1 && zz<=12){
		//cout<<ok2<<endl;
		if(zz==2){
			if(check(year)){
				if(dz>=1 && dz<=29){
					ok2=1;
				//	cout<<ok2<<endl;
				}
			}
			else{
				if(dz>=1 && dz<=28){
					ok2=1;
					//cout<<ok2<<endl;
				}
			}
		}
		else if(zz==4 || zz==6 || zz==9 || zz==11){
			if(dz>=1 && dz<=30) ok2=1;
		}
		else{
			if(dz>=1 && dz<=31) ok2=1;
		}
	}
	
	if(ok2==1){
		day[2].y=year,day[2].m=zz,day[2].d=dz;
		//cout<<year<<"-"<<s[0]<<s[1]<<"-"<<s[3]<<s[4]<<endl;
	}
	hz=a[5]*10+a[6];
	zz=a[3]*10+a[4];
	dz=a[1]*10+a[2];
	if(hz>=0 && hz<=59) year=hz+2000;
	if(hz>=60 && hz<=99) year=hz+1900;
	if(zz>=1 && zz<=12){
		if(zz==2){
			if(check(year)){
				if(dz>=1 && dz<=29){
					ok3=1;
				}
			}
			else{
				if(dz>=1 && dz<=28){
					ok3=1;
				}
			}
		}
		else if(zz==4 || zz==6 || zz==9 || zz==11){
			if(dz>=1 && dz<=30) ok3=1;
		}
		else{
			if(dz>=1 && dz<=31) ok3=1;
		}
	}
//	cout<<year<<"-"<<s[3]<<s[4]<<"-"<<s[0]<<s[1]<<endl;
	if(ok3==1){
		day[3].y=year,day[3].m=zz,day[3].d=dz;
	}
	sort(day+1,day+4,cmp);
	//if()
	for(int i=1;i<=3;i++){
		if((day[i].y==day[i-1].y && day[i].m==day[i-1].m && day[i].d==day[i-1].d) || (day[i].y==day[i-2].y && day[i].m==day[i-2].m && day[i].d==day[i-2].d)) continue;
		if(day[i].d==0) continue;
		printf("%d-%02d-%02d\n",day[i].y,day[i].m,day[i].d);
	}
	return 0;
}