算法刷题day27:日期问题

引言

日期问题在蓝桥杯中只要把常见的题型掌握明白了,把逻辑给写清楚明白,基本上是很简单的,再就是多做题,题型多见,做熟练,基本上问题不大,加油!


概念

日期问题:问题无非就是该日期是否有效,是否回文,日期差值,日期计算这些问题,多做做题,基本上很简单的

答题模板:
只要是日期问题,这样写既清楚明白,又不会乱不会缺啥,再根据题意补充即可。

const int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

bool is_leap(int y)
{
    if(y % 400 == 0 || y % 4 == 0 && y % 100 != 0) return true;
    return false;
}

int get_month_day(int y, int m)
{
    if(m == 2) return days[m] + is_leap(y);
    return days[m];
}

bool is_vaild(int y, int m, int d)
{
    if(m < 1 || m > 12 || d < 1 || d > 31) return false;  // 一般年份都是正确的
    return d <= get_month_day(y,m);
}

bool check(int y, int m, int d)
{
	if(!is_vaild(y,m,d)) return false;
	//该题逻辑
	//......
}

一、日期差值

标签:日期问题

思路:计算从公元 1 1 1 1 1 1 1 1 1 日到 d a t e 1 date1 date1 有多少天,最后两个天数做差再加一即可。

题目描述:

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

输入格式
输入包含多组测试数据。

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

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

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

输入样例:
20110412
20110422
输出样例:
11

示例代码:

#include <bits/stdc++.h>

using namespace std;

int date1, date2;

const int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

bool is_leap(int y)
{
    if(y % 400 == 0 || y % 4 == 0 && y % 100 != 0) return true;
    return false;
}

int get_month_day(int y, int m)
{
    if(m == 2) return days[m] + is_leap(y);
    return days[m];
}

bool is_vaild(int y, int m, int d)
{
    if(m < 1 || m > 12 || d < 1 || d > 31) return false;  // 一般年份都是正确的
    return d <= get_month_day(y,m);
}

int get_total_day(int y, int m, int d)
{
    int res = 0;
    for(int i = 1; i < y; ++i) res += 365 + is_leap(i);
    for(int i = 1; i < m; ++i) res += get_month_day(y,i);
    return res + d;
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    
    while(~scanf("%d", &date1))
    {
        scanf("%d", &date2);
        int y1,m1,d1, y2,m2,d2;
        y1 = date1 / 10000, m1 = date1 % 10000 / 100, d1 = date1 % 100;
        y2 = date2 / 10000, m2 = date2 % 10000 / 100, d2 = date2 % 100;
        cout << abs(get_total_day(y1,m1,d1) - get_total_day(y2,m2,d2)) + 1 << 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 <bits/stdc++.h>

using namespace std;

int a, b, c;

const int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

bool is_leap(int y)
{
    if(y % 400 == 0 || y % 4 == 0 && y % 100 != 0) return true;
    return false;
}

int get_month_day(int y, int m)
{
    if(m == 2) return days[m] + is_leap(y);
    return days[m];
}

bool is_vaild(int y, int m, int d)
{
    if(m < 1 || m > 12 || d < 1 || d > 31) return false;
    return d <= get_month_day(y,m);
}

bool check(int y, int m, int d)
{
    if(!((a == y%100 && b == m && c == d) || (a == m && b == d && c == y%100) 
        || (a == d && b == m && c == y%100)))
        return false;
    if(!is_vaild(y,m,d)) return false;
    return true;
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    
    scanf("%02d/%02d/%02d", &a, &b, &c);
    for(int date = 19600101; date <= 20591231; ++date)
    {
        int y = date / 10000, m = date % 10000 / 100, d = date % 100;
        if(check(y,m,d)) printf("%04d-%02d-%02d\n", y,m,d);
    }
    return 0;
}

三、回文日期 I

标签:日期问题

思路:如果跟上道题一样枚举把日期当作数来枚举的话,如果取极限的话,时间是会超时的,因为要枚举 1 0 8 10 ^ 8 108 个数。一般这种回文日期什么的都是可以光枚举年,然后构造出一个回文日期,看该日期是否在所需范围内,并且是有效日期,这样时间复杂度就就缩减到 O ( 1 0 4 ) O(10 ^ 4) O(104) 了。

题目描述:

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

牛牛习惯用 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 <bits/stdc++.h>

using namespace std;

int date1, date2;

const int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

bool is_leap(int y)
{
    if(y % 400 == 0 || y % 4 == 0 && y % 100 != 0) return true;
    return false;
}

int get_month_day(int y, int m)
{
    if(m == 2) return days[m] + is_leap(y);
    return days[m];
}

bool is_vaild(int y, int m, int d)
{
    if(m < 1 || m > 12 || d < 1 || d > 31) return false;
    return d <= get_month_day(y,m);
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    
    int res = 0;
    cin >> date1 >> date2;
    for(int date = 1; date <= 9999; ++date)
    {
        int t = date, x = date;
        while(t)
        {
            x = x * 10 + t % 10;
            t /= 10;
        }
        if(x < date1 || x > date2) continue;
        
        int y = x / 10000, m = x % 10000 / 100, d = x % 100;
        if(!is_vaild(y,m,d)) continue;
        
        res++;
    }
    
    cout << res << endl;
    return 0;
}

四、回文日期 II

标签:日期问题

思路:根据上一题的思路,可以只遍历年,然后构造出回文日期,然后判断是否是在规定日期后,并且是否有效,最后再判断是否是 A B A B A B A B ABABABAB ABABABAB 型的,另外只需输出一个,给个 f l a g flag flag 标记即可。

题目描述:

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

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

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

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

对此小明很不认同,因为不到 2 年之后就是下一个回文日期: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 <bits/stdc++.h>

using namespace std;

int date1;

const int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

bool is_leap(int y)
{
    if(y % 400 == 0 || y % 4 == 0 && y % 100 != 0) return true;
    return false;
}

int get_month_day(int y, int m)
{
    if(m == 2) return days[m] + is_leap(y);
    return days[m];
}

bool is_vaild(int y, int m, int d)
{
    if(m < 1 || m > 12 || d < 1 || d > 31) return false;
    return d <= get_month_day(y,m);
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    
    cin >> date1;
    bool flag1 = true, flag2 = true;
    for(int year = date1 / 10000; year <= 9999; ++year)
    {
        
        int t = year, x = year;
        while(t)
        {
            x = x * 10 + t % 10;
            t /= 10;
        }
        if(x <= date1) continue;
        
        int y = x / 10000, m = x % 10000 / 100, d = x % 100;
        
        if(!is_vaild(y,m,d)) continue;
        
        if(flag1) cout << x << endl, flag1 = false;
        if(flag2 && y / 100 == y % 100 && y / 1000 != y % 10) cout << x << endl, flag2 = false;
        if(!flag1 && !flag2) break;
    }
    
    return 0;
}

五、日期计算

标签:日期问题

思路:直接枚举每一个月,然后比较天数,如果大了说明是下一个月的,天数减去当前月的总天数,再跟下一个月的天数比较。

题目描述:

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

注意闰年的 2 月有 29 天。

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

年份是 4 的整数倍,而且不是 100 的整数倍;年份是 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 <bits/stdc++.h>

using namespace std;

int year, d;

const int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

bool is_leap(int y)
{
    if(y % 400 == 0 || y % 4 == 0 && y % 100 != 0) return true;
    return false;
}

int get_month_day(int y, int m)
{
    if(m == 2) return days[m] + is_leap(y);
    return days[m];
}

bool is_vaild(int y, int m, int d)
{
    if(m < 1 || m > 12 || d < 1 || d > 31) return false;
    return d <= get_month_day(y,m);
}

int main()
{
    cin >> year >> d;
    
    int month, day;
    for(int i = 1; i <= 12; ++i)
    {
        int t = get_month_day(year, i);
        if(d <= t) 
        {
            month = i, day = d;
            break;
        }
        d -= t;
    }
    
    cout << month << endl;
    cout << day << endl;
    
    return 0;
}
  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lijiachang030718

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

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

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

打赏作者

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

抵扣说明:

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

余额充值