[NOIP2016 普及组T2] 回文日期

给定两个日期,程序需要找出在这两个日期之间(包括这两个日期本身)的所有回文日期的数量。回文日期是指其8位数字形式从左到右和从右到左读都一样的日期。程序需要考虑年份是否为闰年以正确计算2月的天数。

题目描述

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

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

表示方法,而两个不同的日期的表 示方法不会相同。

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

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

例如:

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

每一年中都有 $12$ 个月份:

其中,$1, 3, 5, 7, 8, 10, 12$ 月每个月有 $31$ 天;$4, 6, 9, 11$ 月每个月有 $30$ 天;而对于 $2$ 月,闰年时有 $29$ 天,平年时有 $28$ 天。

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

1. 这个年份是 $4$ 的整数倍,但不是 $100$ 的整数倍;
2. 这个年份是 $400$ 的整数倍。

例如:

- 以下几个年份都是闰年:$2000, 2012, 2016$。
- 以下几个年份是平年:$1900, 2011, 2014$。

输入格式

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

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

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

保证 $\mathit{date}_1$ 和 $\mathit{date}_2$ 都是真实存在的日期,且年份部分一定为 $4$ 位数字,且首位数字不为 $0$。

保证 $\mathit{date}_1$ 一定不晚于 $\mathit{date}_2$。

输出格式

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

样例 #1

样例输入 #1
20110101
20111231

样例输出 #1
1

样例 #2

样例输入 #2
20000101
20101231
样例输出 #2
2

提示

【样例说明】

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

对于样例 2,符合条件的日期是20011002 和 20100102。

【子任务】

对于60% 的数据,满足date1 = \date2。

#include<cstdio>
using namespace std;
int a,b,ay,by,t,s;
bool ly(int yy){//判断闰年
    if (yy%4==0 && yy%100!=0 || yy%400==0) return true;
    return false;
}
bool pn(int nn){//判断回文数
    int ss,aa;
    aa=nn;
    while (aa>0){
        ss=ss*10+aa%10;
        aa/=10;
    }
    if (ss=nn) return true;
    return false; 
}
int month(int yy,int mm){//输出月份,这里也可以使用一个数组,更为简洁,~~我不会告诉你是为我太懒~~
    switch (mm){
        case 1: return 31; break;
        case 2: if (ly(yy)) return 29; else return 28; break;
        case 3: return 31; break;
        case 4: return 30; break;
        case 5: return 31; break;
        case 6: return 30; break;
        case 7: return 31; break;
        case 8: return 31; break;
        case 9: return 30; break;
        case 10: return 31; break;
        case 11: return 30; break;
        case 12: return 31; break;
    }
    return 100;
}
bool date(int nn){//判断日期
    int mm,dd,yy;
    yy=nn/10000;
    mm=nn/100%100;
    dd=nn%100;
    if (mm>12 || mm==0) return false;
    if (dd>month(yy,mm) || dd==0) return false;
    return true;
}
int main(){
    scanf("%d %d",&a,&b);//输入
    ay=a/10000;
    by=b/10000;
    for (int i=ay;i<=by;i++){//枚举
        s=i*10000+(i%10)*1000+(i%100/10)*100+(i/100%10)*10+(i/1000);//变为日期
        if (i==ay && s<a) continue;//跳过特殊情况
        if (i==by && s>b) continue;
        if (date(s)) t++;
    }
    printf("%d\n",t);//输出
    return 0;
}

<think>好的,用户想要找NOIP2016普及组回文日期问题的C++实现代码。首先,我需要回忆一下这个问题的大致要求。题目应该是给定两个日期,找出这两个日期之间(包括这两个日期)的所有回文日期的数量。回文日期指的是日期格式为yyyymmdd的字符串,正读和反读都一样的日期。 接下来,我需要检查用户提供的引用内容。用户引用了四个来源,其中引用[2]直接提到了NOIP2016回文日期的问题,并附带了代码。看起来用户已经提供了相关的代码,但可能需要更详细的解释或确认代码的正确性。 首先,我需要分析引用[2]中的代码是否正确。代码的大致思路是枚举每个可能的,然后根据生成对应的年份,构造完整的日期,再检查是否在给定范围内。例如,份为12日期21,那么年份就是1221(因为回文要求,年份是的逆序),所以整个日期就是12211221,这样的日期是否符合要求? 不过需要注意的是,代码中处理闰年的部分是否正确。引用中的代码使用了一个份天数表m[],其中229天,并注释说忽略闰年问题。这可能有问题,因为实际题目中需要考虑闰年的情况吗?比如,如果生成的日期是0229,对应的年份是9220,那么是否9220年是闰年?这里可能需要更严谨的处理,但题目中是否要求严格检查闰年呢? 根据NOIP2016题目要求,可能需要正确处理闰年。例如,生成的日期是否合法,包括闰年的229。但用户提供的代码中可能简化了这一点,直接使用固定天数,可能导致某些情况下的错误。例如,如果生成的份是2日期29,但对应的年份不是闰年,那么这个日期实际上是非法的,应该被排除。 因此,用户提供的代码可能存在缺陷,因为它没有检查生成的年份是否为闰年,以确定229是否有效。但根据NOIP的测试用例,可能这个简化方法也能过,因为题目可能没有设计这样的边缘情况,或者数据中不包含这样的非法日期。需要进一步确认题目的具体要求。 接下来,用户需要的是正确的C++实现代码。因此,我需要提供一个考虑闰年情况的代码,或者指出用户提供的代码中的潜在问题并给出改进建议。 此外,用户可能希望代码有注释和解释,以帮助他们理解实现思路。因此,在回答时,应该分步骤解释代码的逻辑,包括如何生成回文日期、如何检查日期是否在范围内,以及如何处理闰年问题。 总结步骤: 1. 枚举所有可能的份和日期组合,因为回文日期的格式是yyyymmdd,而回文要求前四位是后四位的逆序。因此,可以将(mm)和(dd)确定后,年份就是ddmm的反转。例如,为1221,年份为2112,所以日期21121221。 2. 生成完整的8位数字日期,并检查是否在输入的两个日期范围内。 3. 需要确保生成的份和日期是有效的,比如份在1-12之间,日期不超过该的最大天数,并且考虑闰年的2情况。 4. 统计所有符合条件的日期数量。 现在,检查用户提供的代码: 代码中使用month从1到12循环,day从1到m[month],其中m数组的229天。然后,year的计算方式是day的个位乘以1000加上十位乘以100,再加上month的个位乘以10和month的十位。例如,month=12,day=21,那么year= (21%10)*1000 + (21/10)*100 + (12%10)*10 +12/10 → 1*1000 + 2*100 + 2*10 +1 → 1000+200+20+1=1221,所以date=1221*10000 +12*100 +21=12211221,这个是正确的。 但问题在于,当生成的份和日期是0229时,对应的年份是9220年,需要检查9220年是否为闰年。然而,代码中没有进行闰年检查,而是直接假设所有2都有29天。这可能在某些情况下导致统计到无效的日期,比如当生成的年份不是闰年时,0229这个日期是不合法的。 但根据NOIP2016题目描述,可能测试数据中没有包含这样的边缘情况,或者题目允许忽略这一点。例如,题目可能只要求生成的组合在对应的份天数范围内,而不管年份是否实际为闰年。或者,可能题目中的回文日期生成方式导致年份足够大,使得9220年可能被视为闰年,但实际公历闰年的计算方式是年份能被4整除但不能被100整除,或者能被400整除。例如,9220年是否能被4整除?9220 ÷4=2305,余0,所以是闰年。因此,这种情况下,0229是有效的。因此,可能代码中的处理是正确的,因为生成的年份为反转后的,所以年份可能自动满足闰年条件。例如,当day是29,month是02时,年份是9220,而9220是闰年,所以0229是有效日期。这可能吗? 例如,month=02,day=29,生成年份为9220。9220年是否为闰年?根据闰年规则,能被4整除但不能被100整除,或者能被400整除。9220 ÷4=2305,余0。但9220 ÷100=92.2,所以不是整百年,因此是闰年。因此,9220年的229是有效的。因此,在代码中处理份为229时,如果生成的年份是闰年,则有效。这种情况下,代码中的处理是否正确? 但问题在于,当生成的份和日期是其他情况时,例如month=02,day=30,这显然无效,因为2最多29天,所以day不会超过m[month]的值,这里m[2]=29,所以day循环到29,所以不会出现30的情况。因此,代码中的m数组已经限制了每个的天数,因此生成的day不会超过该的实际天数。然而,对于闰年而言,如果份是2,day=29,而对应的年份是非闰年,则这个日期是非法的。但根据上述分析,当份是02,day=29时,生成的年份是9220,是闰年,所以合法。因此,代码中的处理是正确的? 或者,可能存在其他情况,比如month=02,day=29,生成的年份是其他年份,如1329,这时年份是9132?例如,month=02,day=29,那么年份是 (29%10)*1000 + (29/10)*100 + (02%10)*10 +02/10 → 9*1000 + 2*100 + 2*10 +0 → 9200+200+20=9220,如前所述,正确。所以份和日期的组合是02-29,生成的年份是9220,闰年,合法。因此,代码中的处理是正确的。 因此,用户提供的代码可能已经正确处理了闰年的情况,因为当且仅当生成的份是02日期29时,对应的年份自动满足闰年条件,因此日期有效。所以代码中的处理是正确的,无需额外检查闰年? 这可能是一个巧妙的设计,即枚举所有可能的组合,生成对应的年份,并构造日期,其中年份由决定,并且由于代码中份的天数表已经限制了每个的最大天数(例如,229天),因此生成的日期中的年份必然是闰年,当且仅当份是2日期29时。因此,这种情况下,年份满足闰年条件,从而日期有效。例如,当month=02,day=29时,生成的年份是9220,是闰年,因此日期有效。对于其他份来说,天数已经被限制,不会出现无效的情况,例如4不会有31天,因为m[4]=30。 因此,用户提供的代码是正确的。这可能是因为题目中的回文日期的构造方式导致年份必须满足相应的份的天数,因此无需额外检查闰年。 因此,现在需要将用户提供的代码进行整理,并添加注释,解释其逻辑,以帮助用户理解。同时,可能需要指出代码中的处理方式如何巧妙避免了复杂的闰年判断。 总结回答步骤: 1. 给出代码,并解释其思路。 2. 解释代码如何生成回文日期,即枚举,生成年份,构造完整日期。 3. 说明如何检查日期是否在输入范围内。 4. 解释代码如何处理闰年问题(即过固定份的天数表,利用回文日期的特性自动满足年份的闰年条件)。 5. 提供相关问题,如优化方法、其他解法等。 最后,根据用户的要求,生成相关问题。</think>### NOIP2016普及组回文日期C++实现代码 **问题描述**:给定两个日期 $d_1$ 和 $d_2$,统计在 $[d_1, d_2]$ 范围内所有满足以下条件的回文日期数量: - 日期格式为 $yyyymmdd$,且正读和反读完全相同(例如 `20200202` 不是回文,而 `92200229` 是回文)。 **实现思路**: 1. **逆向构造日期**:回文日期的前四位(年份)是后四位()的逆序。例如,若为 `12`、为 `21`,则年份为 `2112`,完整日期为 `21121221`。 2. **枚举组合**:遍历所有可能的(1-12)和(1-该最大天数),生成对应年份,构造完整日期。 3. **闰年处理**:直接过预定义份天数表(2固定为29天),利用回文特性保证年份合法性。例如,`0229` 生成的年份 `9220` 是闰年,因此合法[^2]。 **C++代码实现**: ```cpp #include <bits/stdc++.h> using namespace std; int main() { int d1, d2, cnt = 0; cin >> d1 >> d2; int month_days[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 份天数表(229天处理) for (int month = 1; month <= 12; month++) { for (int day = 1; day <= month_days[month]; day++) { // 根据生成年份(例如:month=12, day=21 → 年份=1221) int year = (day % 10) * 1000 + (day / 10) * 100 + (month % 10) * 10 + (month / 10); int date = year * 10000 + month * 100 + day; // 构造完整日期yyyymmdd if (date >= d1 && date <= d2) { cnt++; } } } cout << cnt; return 0; } ``` **代码解析**: - **份天数表**:`month_days` 定义每个的天数,229天处理。由于回文日期的年份由决定,生成的年份会自动满足闰年条件(如 `0229` 对应年份 `9220` 是闰年)[^2]。 - **日期合法性**:过枚举时限制天数范围,保证生成的日期合法。 - **性能优化**:仅需枚举 $12 \times 31 = 372$ 种组合,时间复杂度为 $O(1)$。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值