《算法笔记》学习日记——3.4 日期处理

3.4 日期处理

Codeup Contest ID:100000578

问题 A: 日期差值

题目描述
有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天。
输入
有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD
输出
每组数据输出一行,即日期差值
样例输入

20130101
20130105

样例输出

5

思路
思路参考了《2013年王道论坛计算机考研机试指南》中的内容,主要的想法是以空间换时间:先定义一个date的结构体,然后声明int型的年、月、日,还要再声明一个成员函数nextday(),用来计算下一天(day++只后主要判断是不是下个月或者是不是下一年就好了)。

然后最主要的思路来了:既然是求解两个日期之间的差值,那么可以先求解从原点日期分别到两个日期之间的天数,然后再相减,就是两个日期之间的天数差(就和之前的Shortest Distance一样,先预处理好所有的距离,最后通过减法得出结果,这样运算效率大大加快,缺点是要开辟很多的内存空间)。
代码

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
//第二维的0是平年,1是闰年 
int isleapyear(int x){
	if((x%4==0&&x%100!=0)||(x%400==0)) return 1;
	else return 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 year;
	int month;
	int day;
	void nextday(){
		day++;
		if(day>dayofmonth[month][isleapyear(year)]){
			day = 1;
			month++;
			if(month>12){
				month = 1;
				year++;
			}
		}
	}
}temp;
int buf[10001][13][32];//用来存放从0年1月1日到所有日期之间的天数
//这里最好第一维的年份开大一点,5000是不够用的,所以我找了半天错误不知道错在哪了... 
int main(){
	int cnt = 0;//与原点的天数差 
	temp.year = 0;
	temp.month = 1;
	temp.day = 1;
	while(temp.year!=10000){
	//上面改了年份大小之后记得这里也要改,不然会数组会爆的 
		buf[temp.year][temp.month][temp.day] = cnt;
		temp.nextday();
		cnt++;
	}
	int a, b, c, x, y, z;
	while(scanf("%4d%2d%2d\n%4d%2d%2d", &a, &b, &c, &x, &y, &z) != EOF){
		printf("%d\n", abs(buf[a][b][c] - buf[x][y][z])+1);//记得要取绝对值再加1
	}
	return 0;
}

问题 B: Day of Week

题目描述
We now use the Gregorian style of dating in Russia. The leap years are years with number divisible by 4 but not divisible by 100, or divisible by 400.
For example, years 2004, 2180 and 2400 are leap. Years 2004, 2181 and 2300 are not leap.
Your task is to write a program which will compute the day of week corresponding to a given date in the nearest past or in the future using today’s agreement about dating.
输入
There is one single line contains the day number d, month name M and year number y(1000≤y≤3000). The month name is the corresponding English name starting from the capital letter.
输出
Output a single line with the English name of the day of week corresponding to the date, starting from the capital letter. All other letters must be in lower case.
样例输入

21 December 2012
5 January 2013

样例输出

Friday
Saturday

思路
我的想法比较简单,和上题类似,首先以1000年1月1日作为原点日期,然后一直存储到3000年12月31日的天数。至于星期几怎么算呢,我认为可以以当前日期为标准,然后计算输入日期和今天日期的天数差,分两种情况往前推或者往后推。
比如今天是2020年3月13日,是礼拜五,对应下标是5,假设输入的是12 March 2020,那么天数差就是-1,说明要往前推,取绝对值为1,那么week[5-1]就是对应的星期几,如果天数差大于7,那就除7取余数即可。以此类推,一般情况下,如果往前推,week[5-tsc%7]就是对应的星期几。
但是这个方法要注意数组会不会越界,如果越界的话自己修正一下就好了。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int isleapyear(int x){
	if((x%4==0&x%100!=0)||x%400==0) return 1;
	else return 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
};
char nameofmonth[13][10] = {"0","January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
char week[7][10] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
struct date{
	int year;
	int month;
	int day;
	void nextday(){
		day++;
		if(day>dayofmonth[month][isleapyear(year)]){
			day = 1;
			month++;
			if(month>12){
				month = 1;
				year++;
			}
		}
	}
}temp;
int buf[3002][13][32];
int main(){
	int d, y, cnt;
	cnt = 0;
	temp.year = 1000;
	temp.month = 1;
	temp.day = 1;
	while(temp.year!=3001){
		buf[temp.year][temp.month][temp.day] = cnt;
		temp.nextday();
		cnt++;
	}
	char mm[10];
	while(scanf("%d %s %d", &d, mm, &y) != EOF){
		int m;
		for(int i=1;i<=12;i++){
			if(strcmp(nameofmonth[i], mm)==0){
				m = i;
				break;
			}
		}
		int tsc;//记录输入日期和当前日期的天数差
		tsc = buf[y][m][d] - buf[2020][3][13];
		if(tsc<0){
			tsc = abs(tsc);
                   //如果超出数组范围,就人为修正:
			if(5-tsc%7<0) printf("%s\n", week[5-(5-tsc%7)]);
			else printf("%s\n", week[5-tsc%7]);
		}
		else{
                   //如果超出数组范围,就人为修正:
			if(5+tsc%7>6) printf("%s\n", week[5+tsc%7-7]);
			else printf("%s\n", week[5+tsc%7]);
		}
	}
	return 0;
}

问题 C: 打印日期

题目描述
给出年分m和一年中的第n天,算出第n天是几月几号。
输入
输入包括两个整数y(1<=y<=3000),n(1<=n<=366)。
输出
可能有多组测试数据,对于每组数据,按 yyyy-mm-dd的格式将输入中对应的日期打印出来。
样例输入

2013 60
2012 300
2011 350
2000 211

样例输出

2013-03-01
2012-10-26
2011-12-16
2000-07-29

思路
这题比前两题都要简单,不需要存储天数差,只要用for循环执行n次nextday()函数,再输出对应的年月日即可,注意,格式是yyyy-mm-dd,也就是在printf中需要%04d-%02d-%02d(如果不加0是空格补充)。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int isleapyear(int x){
	if((x%4==0&&x%100!=0)||x%400==0) return 1;
	else return 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 year;
	int month;
	int day;
	void nextday(){
		day++;
		if(day>dayofmonth[month][isleapyear(year)]){
			day = 1;
			month++;
			if(month>12){
				month = 1;
				year++;
			}
		}
	}
}temp;
int main(){
	int y, tsc;
	while(scanf("%d%d", &y, &tsc) != EOF){
		temp.year = y;
		temp.month = 1;
		temp.day = 1;
		for(int i=1;i<tsc;i++) temp.nextday();
		printf("%04d-%02d-%02d\n", temp.year, temp.month, temp.day);
	}
	return 0;
}

问题 D: 日期类

题目描述
编写一个日期类,要求按xxxx-xx-xx 的格式输出日期,实现加一天的操作。
输入
输入第一行表示测试用例的个数m,接下来m行每行有3个用空格隔开的整数,分别表示年月日。测试数据不会有闰年。
输出
输出m行。按xxxx-xx-xx的格式输出,表示输入日期的后一天的日期。
样例输入

2
1999 10 20
2001 1 31

样例输出

1999-10-21
2001-02-01

思路
这一题更简单,用一次nextday()函数即可,而且测试数据不包含闰年,但是为了避免重复编写浪费时间,我直接沿用上一题的nextday()函数了(包含闰年的判断)。要注意的是,m行是要一起输出的,而不是输入一行输出一行,所以我们需要一个数组存储计算的数据。这里我选择直接用sprintf函数以指定的格式将三个int型要素存入字符串数组中,非常方便。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int isleapyear(int x){
	if((x%4==0&&x%100!=0)||x%400==0) return 1;
	else return 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 year;
	int month;
	int day;
	void nextday(){
		day++;
		if(day>dayofmonth[month][isleapyear(year)]){
			day = 1;
			month++;
			if(month>12){
				month = 1;
				year++;
			}
		}
	}
};
int main(){
	int m;
	while(scanf("%d", &m) != EOF){
		char str[m][20];
		int len = 0;
		for(int i=1;i<=m;i++){  //这里最好不要用while(m--),这样会改变m的值,而m下面还要用到
			int y, mm, d;
			struct date temp;
			scanf("%d%d%d", &y, &mm, &d);
			temp.year = y;
			temp.month = mm;
			temp.day = d;
			temp.nextday();
                   //用sprintf以格式"%04d-%02d-%02d"存入字符串数组str[len]中:
			sprintf(str[len], "%04d-%02d-%02d", temp.year, temp.month, temp.day);
			len++;
		}
		for(int i=0;i<m;i++) printf("%s\n", str[i]);
	}
	return 0;
}

问题 E: 日期累加

题目描述
设计一个程序能计算一个日期加上若干天后是什么日期。
输入
输入第一行表示样例个数m,接下来m行每行四个整数分别表示年月日和累加的天数。
输出
输出m行,每行按yyyy-mm-dd的个数输出。
样例输入

1
2008 2 3 100

样例输出

2008-05-13

思路
这题和上一题几乎一模一样,只不过上题执行的是一次nextday(),这次要用for循环执行指定次数的nextday(),真的是只多了一行代码……(scanf里多加的不算)
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int isleapyear(int x){
	if((x%4==0&&x%100!=0)||x%400==0) return 1;
	else return 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 year;
	int month;
	int day;
	void nextday(){
		day++;
		if(day>dayofmonth[month][isleapyear(year)]){
			day = 1;
			month++;
			if(month>12){
				month = 1;
				year++;
			}
		}
	}
};
int main(){
	int m;
	while(scanf("%d", &m) != EOF){
		char str[m][20];
		int len = 0;
		for(int i=1;i<=m;i++){
			int y, mm, d, tsc;
			struct date temp;
			scanf("%d%d%d%d", &y, &mm, &d, &tsc);
			temp.year = y;
			temp.month = mm;
			temp.day = d;
			for(int i=1;i<=tsc;i++) temp.nextday();//只多了for循环这一句话 
			sprintf(str[len], "%04d-%02d-%02d", temp.year, temp.month, temp.day);
			len++;
		}
		for(int i=0;i<m;i++) printf("%s\n", str[i]);
	}
	return 0;
}

小结

经过几个题目的反复练习之后,可以发现,其实日期处理的问题大同小异:声明一个是否是闰年的计算函数isleapyear、声明一个二维数组dayofmonth存储月份对应的天数、声明一个结构体date,里面包含了年月日三个要素和一个成员函数nextday(),然后再根据需要确定是否要开一个三维数组存储所有天数差(像最后几题如果直接给出确定的日期和确定的天数差,只要用for循环直接控制nextday()的执行次数即可)。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值