特殊日期

原题

对于一个日期,我们可以计算出年份的各个数位上的数字之和,也可以分别计算月和日的各位数字之和。请问从 蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_日期 年 蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_日期_02 月 蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_日期_02 日至 蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_枚举优化_04 年 蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_枚举优化_05 月 蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_枚举优化_06 日,总共有多少天,年份的数位数字之和等于月的数位数字之和加日的数位数字之和。

例如,蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_枚举优化_07 年 蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_枚举优化_08 月 蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_日期_09 日满足要求,因为 蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_异常语句_10 。

请提交满足条件的日期的总数量。


分析

这题关键在于枚举符合条件的“日期”(年份的各个数位上的数字之和=月、日的数位数字之和),若一一枚举出题意范围内的日期,时间复杂度太大,不切实际。所以我们需要压缩枚举的范围。

首先,月、日的数位数字之和的最大值和最小值可以人为确定下来,分别为20和2(分别由9月29日和1月1日两个日期得出),因为许多年份的各个数位上的数字之和是大于20的,所以可以直接排除这些年份。

其次,不管哪一年,日期种类数不会超过12*31=372种,所以这里先把这372种日期(月、日)及其各个数位上的数字之和的值枚举出来,然后枚举一定范围内的年份,将符合条件的日期(年、月、日)枚举出来。具体实现见如下代码和详细注释。


源码

# 特殊日期
import datetime

sum_min = 2
sum_max = 20   # 月、日最大的数位和,9月29日(0929,9+2+9=20)

# 将每个月份中的每天(month,day)和数位和sum_存储在字典dict_sum2days中
dict_sum2days = {}
# 初始化字典
for sum_ in range(sum_min,sum_max+1):
    dict_sum2days[sum_] = []
# sum_(范围在[sum_min,sum_max])作为“键”,存放(month,day)的列表作为“值”
# 一年12*31天(为方便统计,假设一个月31天,后续再排除不存在的日期)
for month in range(1,13):
    for day in range(1,32):
        sum_ = sum( map(int,str(month)+str(day)) )  # 计算月、日的数位和
        dict_sum2days[sum_].append( (month,day) )


count = 0

# 遍历年份
for year in range(1900,9921):
    sum_ = sum( map(int,str(year)) )
    if sum_ > sum_max:
        continue
    # 取出与当前 年份的各个数位上的数字之和相等的月、日(m,d)
    for m,d in dict_sum2days[sum_]:
        # 使用“异常”排除不存在的日期
        try:
            datetime.date(year,m,d)  # 若日期(year,m,d)不存在,该语句将报错,except语句捕捉后跳过该日期
            count += 1
        except:
            continue

print(count)


特殊时间

原题

蓝桥杯备战日志(Python)23-特殊时间&特殊日期-(枚举特殊“时间”)_异常语句_11


源码

解题思路其实和上一题“特殊日期”有许多相似之处,算法具体实现如下。

import datetime

def is_legal_str(s: str):
    '''判断一个字符串是否是满足条件的字符串'''
    if len(set(s)) == 2:
            num = s.count(s[0])
            if num == 1 or num == 3:
                return True
    return False

month_day_l = []  # 满足条件的月和日组成的4位数序列(先统一考虑所有月份都为31天)
# 其实不用遍历到31天,因为不存在可以组合成满足条件的“4位数”
for m in range(1,13):
    for d in range(1,32):
        # 日期字符串(月、日)
        date_str = "%02d%02d" % (m,d)
        # 判断该日期字符串是否满足条件
        if is_legal_str(date_str):
            month_day_l.append( (m,d,date_str) )


date_l = []  # 满足条件的日期字符串(年、月、日)
for year in range(9999):
    year_str = "%04d"%year
    if is_legal_str(year_str):
        for m,d,date_str in month_day_l:
            try:
                datetime.date(year,m,d)
            except:
                continue
            # 要保证两个“4位数”由相同的两种数字组合
            if sorted(year_str) == sorted(date_str):
                date_l.append(year_str+date_str)
        
count = 0
# res_l = []  # 存储满足条件的时间
# 时间范围: 00:00 ~ 23:59
for h in range(24):
    for m in range(60):
        # 时间字符串,22时02分,形如2202
        time_str = "%02d%02d"%(h,m)
        # 判断该时间字符串是否满足条件
        if is_legal_str(time_str):
            for date_str in date_l:
                if sorted(date_str[:4]) == sorted(time_str):
                    count += 1
#                     res_l.append(date_str+time_str)

print(count)


上一篇:蓝桥杯备战日志(Python)22-最小权值&本质上升序列-(二叉树的形式、动态规划)