(二)王道机试指南___日期类计算

算例1

  • 题目描述

  • 解题思路

①解决这类区间问题有一个统一的思想——把原区间问题统一到确定的区间问题,即把问题统一到特定的日期与一个原点(如0000年1 月1日)的天数差,然后将两个天数差相减,这有一个巨大的好处,就是预处理,我们可以在程序真正开始处理输入数据之前,预处理出所有日期与原点日期之间的天数差并保存起来。当数据真正开始输入时,我们只需要用O(1)的时间复杂度将保存的数据读出,稍加处理便能得到答案。(预处理是一种空间换时间的重要手段)

②闰年判断:year%100!=0&&year%4==0 || year%400==0

  • 解题代码

#include <stdio.h>
#define ISYEAP(x) x % 100 != 0 && x % 4 == 0 || x % 400 == 0 ? 1 : 0
// 定义宏判断是否是闰年,方便计算每月天数
int dayOfMonth[13][2] = {
	0,0,
	31,31,
	28,29,
	31,31,
	30,30,
	31,31,
	30,30,
	31,31,
	31,31,
	30,30,
	31,31,
	30,30,
	31,31
}; //预存每月的天数,注意二月配合宏定义作特殊处理
struct Date { //日期类,方便日期的推移
	int Day;
	int Month;
	int Year;
	void nextDay() { //计算下一天的日期
		Day++;
		if (Day > dayOfMonth[Month][ISYEAP(Year)]) { //若日数超过了当月最大日数
				Day = 1;
			Month++; //进入下一月
			if (Month > 12) { //月数超过12
				Month = 1;
				Year++; // 进入下一年
			}
		}
	}
};
int buf[5001][13][32]; //保存预处理的天数
int Abs(int x) { //求绝对值
	return x < 0 ? -x : x;
}
int main() {
	Date tmp;
	int cnt = 0; //天数计数
	tmp.Day = 1;
	tmp.Month = 1;
	tmp.Year = 0; //初始化日期类对象为0年1月1日
	while (tmp.Year != 5001) { //日期不超过5000年
		buf[tmp.Year][tmp.Month][tmp.Day] = cnt; //将该日与0年1月1日的天数差保存起来
			tmp.nextDay(); //计算下一天日期
		cnt++; //计数器累加,每经过一天计数器即+1,代表与原点日期的间隔又增加一天
	}
	int d1, m1, y1;
	int d2, m2, y2;
	while (scanf("%4d%2d%2d", &y1, &m1, &d1) != EOF) {
		scanf("%4d%2d%2d", &y2, &m2, &d2); //读入要计算的两个日期
		printf("%d\n", Abs(buf[y2][m2][d2] - buf[y1][m1][d1]) + 1); //用预处理的数据计算两日期差值, 注意需对其求绝对值
	}
	return 0;
}
  • 注意点

①案例代码和自己写的代码相比有很多的优点,比如宏定义闰年判断,DayOfMonth二维数组的定义,保存预处理的天数buf数组的引入,以及整个思路的转换就避免了很多闰年天数月数的计算,十分值得学习!!

记!输入时在%d之间插入数字来读取特定位数的数字,输出时可以在数字前加0表示空位补0(%04d)

记!善于运用多维数组存储数据,例如月份、天数、星期

④buf数组需要耗费大量内存,若在main函数中定义,容易导致栈溢出,因此凡是涉及此类需要开辟大量内存空间的情况,我们都必须在函数体外定义,即定义为全局变量,或者在函数中使用malloc等函数动态申请变量空间

算例2

  • 题目描述

  • 解题思路

①利用上题中buf思想,由于星期是循环的,故可以用已知的日期星期号推输入日期的星期号

  • 解题代码

#include <stdio.h>
#include <string.h>
#define ISYEAP(x) x % 100 != 0 && x % 4 == 0 || x % 400 == 0 ? 1 : 0
int dayOfMonth[13][2] = {
	0,0,
	31,31,
	28,29,
	31,31,
	30,30,
	31,31,
	30,30,
	31,31,
	31,31,
	30,30,
	31,31,
	30,30,
	31,31
};
struct Date {
	int Day;
	int Month;
	int Year;
	void nextDay() {
		Day++;
		if (Day > dayOfMonth[Month][ISYEAP(Year)]) {
			Day = 1;
			Month++;
			if (Month > 12) {
				Month = 1;
				Year++;
			}
		}
	}
};
int buf[3001][13][32];
char monthName[13][20] = {
	"" ,
	"January" ,
	"February" ,
	"March" ,
	"April" ,
	"May" ,
	"June" ,
	"July" ,
	"August" ,
	"September" ,
	"October" ,
	"November" ,
	"December"
}; //月名每个月名对应下标1到12
char weekName[7][20] = {
	"Sunday" ,
	"Monday" ,
	"Tuesday" ,
	"Wednesday" ,
	"Thursday" ,
	"Friday" ,
	"Saturday"
}; //周名每个周名对应下标0到6
int main() {
	Date tmp;
	int cnt = 0;
	tmp.Day = 1;
	tmp.Month = 1;
	tmp.Year = 0;
	while (tmp.Year != 3001) {
		buf[tmp.Year][tmp.Month][tmp.Day] = cnt;
		tmp.nextDay();
		cnt++;
	} //以上与上题一致,预处理出每一天与原点日期的天数差
	int d, m, y;
	char s[20];
	while (scanf("%d%s%d", &d, s, &y) != EOF) {
		for (m = 1;m <= 12;m++) {
			if (strcmp(s, monthName[m]) == 0) {
				break; //将输入字符串与月名比较得出月数
			}
		}
		int days = buf[y][m][d] - buf[2012][7][16]; //计算给定日期与今日日期的天数间隔(注意可能为负)
			days += 1; //今天(2012.7.16)为星期一,对应数组下标为1,则计算1经过days天后的下标
			puts(weekName[(days % 7 + 7) % 7]); //将计算后得出的下标用7对其取模,并且保证其为非负数, 则该下标即为答案所对应的下标, 输出即可
	}
	return 0;
}
  • 注意点

①关于日期、月份数的存储可以使用数组,而不是switch-case

猜你喜欢

转载自blog.csdn.net/weixin_40614231/article/details/83582953