编程之美2015资格赛-题目1 2月29日

博客作者分享了在编程之美资格赛中遇到的题目1——关于2月29日的问题。作者最初的做法复杂且易遗漏情况,导致错误。后来发现一种简洁的解法,无需判断闰年,仅用几行代码即可解决。作者计划资格赛后更新博客分享AC解法,并以此反思自身编程技巧的不足。

这道题,我做复杂了。而且自己的做法要判断的情况太多,一个不小心就会有所遗漏。虽然后来补全了一些遗漏的情况,但提交老是WA,应该是还有什么情况漏掉了。总之,自己这种做法太过笨拙,而且吃力不讨好。

后来我看到有一种思路,不用具体判断闰年,因为题目本身只是要给出有多少个而已,这样的解法比我自己的解法巧妙多了,而且短短几行coding就可以解决。具体资格赛完了之后再改一下博客贴出来。

先贴个自己写的一直WA显然遗漏什么情况的版本,也以示警戒吧。还是得多加修炼才行啊。

题目1 : 2月29日

时间限制: 2000ms
单点时限: 1000ms
内存限制: 256MB

描述

给定两个日期,计算这两个日期之间有多少个2月29日(包括起始日期)。

只有闰年有2月29日,满足以下一个条件的年份为闰年:

1. 年份能被4整除但不能被100整除

2. 年份能被400整除

输入

第一行为一个整数T,表示数据组数。

之后每组数据包含两行。每一行格式为"month day, year",表示一个日期。month为{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November" , "December"}中的一个字符串。day与year为两个数字。

数据保证给定的日期合法且第一个日期早于或等于第二个日期。

输出

对于每组数据输出一行,形如"Case #X: Y"。X为数据组数,从1开始,Y为答案。

数据范围

1 ≤ T ≤ 550

小数据:

2000 ≤ year ≤ 3000

大数据:

2000 ≤ year ≤ 2×109

样例输入
4
January 12, 2012
March 19, 2012
August 12, 2899
August 12, 2901
August 12, 2000
August 12, 2005
February 29, 2004
February 29, 2012
样例输出
Case #1: 1
Case #2: 0
Case #3: 1
Case #4: 3
#include<cstring>
#include<iostream>
using namespace std;
struct Month{
	char* strMonth;
	int nMonth;
}reserved[12] = {
	{ "January", 1 }, { "February", 2 },
	{ "March", 3 }, { "April", 4 },
	{ "May", 5 }, { "June", 6 },
	{ "July", 7 }, { "August", 8 },
	{ "September", 9 }, { "October", 10 },
	{ "November", 11 }, { "December", 12 }
};

struct Date{
	int month;
	int day;
	int year;
};

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

//assume that the year is different,and they are both leap years
int incl_st_end(Date date1, Date date2){
	int m1 = date1.month, m2 = date2.month,
		d1 = date1.day, d2 = date2.day;
	if (m1 <= 2 && (m2 > 2 || (m2 == 2 && d2 == 29)))
		return 1;
	else if (m1 > 2 && (m2 < 2 || (m2 == 2 && d2 < 29)))
		return -1;
	else return 0;
}

int howManyLeaps(Date date1, Date date2){
	int d1 = date1.day, d2 = date2.day,
		m1 = date1.month, m2 = date2.month,
		y1 = date1.year, y2 = date2.year;
	int cnt;
	int howMany = 0;
	int beginLeap = y1, endLeap = y2;
	Date begin, end;
	if (y1 == y2)//same year
	{
		if (isLeap(y1))//leap year
		{
			if (m1 == m2)//same month
			{
				if (m1 == 2){
					if (d2 != 29)//one
						return 0;
					else//none
						return 1;
				}
				else 
					return 0;
			}
			else //different months
			{
				if (m1 <= 2 && m2 > 2)
					return 1;
				else
					return 0;
			}
		}
		else //not Leap year
			return 0;
	}
	else //not the same year
	{
		//judge if we have to include the begin year and the end year
		for (; beginLeap < y2; beginLeap++){
			if (isLeap(beginLeap))
				break;
		}
		for (; endLeap > y1; endLeap--){
			if (isLeap(endLeap))
				break;
		}
		if (beginLeap>endLeap)//if no leap years	
			return 0;
		if (beginLeap == y1&&endLeap == y2){
			begin = date1;
			end = date2;
		}
		else if (beginLeap == y1&&endLeap != y2){
			end.day = 31;
			end.month = 12;
			end.year = endLeap;
			begin = date1;
		}
		else if(beginLeap!=y1&&endLeap==y2){
			begin.day = 1;
			begin.month = 1;
			begin.year = beginLeap;
			end = date2;
		}
		else {
			begin.day = 1;
			begin.month = 1;
			begin.year = beginLeap;
			end.day = 31;
			end.month = 12;
			end.year = endLeap;
		}
		cnt = incl_st_end(begin, end);
		for (int tmp(begin.year); tmp < end.year; tmp = tmp + 4){
			if (isLeap(tmp))
				howMany++;
		}
		howMany += cnt;
		return howMany;
	}
}

int main(){
	int t,i,j,num;
	char strMonth[10]="";
	int month, day, year;
	char delimeter;
	Date d[2];
	cin >> t;
	for (i = 0; i < t; i++){
		for (j = 0; j < 2; j++){
			cin >> strMonth >> day >> delimeter >> year;
			for (int i(0); i < 12; i++){
				if (strcmp(strMonth, reserved[i].strMonth) == 0){
					month = reserved[i].nMonth;
					break;
				}
			}
			d[j].day = day;
			d[j].month = month;
			d[j].year = year;
		}
		num = howManyLeaps(d[0], d[1]);
		cout << "Case #" << i+1 << ":" << " " << num << endl;
	}
}

网上看到的一种AC解法:

#include <cstdio>
#include <cstring>
int main()
{
	char start_month[12], end_month[12];
	int start_day, start_year, end_day, end_year;
	int T, count;
	while(scanf("%d", &T)!=EOF)
	{
		for(int i=1; i<=T; ++i)
		{
			scanf("%s %d, %d", start_month, &start_day, &start_year);
			scanf("%s %d, %d", end_month, &end_day, &end_year);
			count = 0;
			if(strcmp(start_month,"January")==0 || (strcmp(start_month,"February")==0&&start_day<=29))
			{
				--start_year;
			}
			if(strcmp(end_month,"January")==0 || (strcmp(end_month,"February")==0&&end_day<29))
			{
				--end_year;
			}
			count = (end_year>>2)-(start_year>>2);
			count = count -(end_year/100-start_year/100);
			count += ((end_year/400-start_year/400));
			printf("Case #%d: %d\n", i, count);
		}
	}
	return 0;
}
转自这里的。

点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值