**
第A练——日期与时间
**
- 日期与时间——必做题
1.1. 判断天数:TheDay.cpp(本题50分) 【题目描述】 输入某“年”某“月”某“日”,判断这一天是这一年的第几天?
【程序分析】
以3月5日为例,应该先把前两个月的加起来,然后再加上5天即本年的第几天,特殊情况,闰年且输入月份大于3时需考虑多加一天。建议使用switch分支语句。
【输入】
输入文件TheDay.in有1行,包含3个int类型的整数,各整数之间由一个英文状态下的空格隔开,即输入的年月日变量year, month, day。
【输出】
输出文件TheDay.out有1行,包含1个int类型的整数(整型变量sum),即这一天是这一年的第几天?
【输入输出样例1】
TheDay.in TheDay.out
2016 4 5 96
【输入输出样例2】
TheDay.in TheDay.out
1997 2 18 49
【数据限制】
year≥1,1≤month≤12,1≤day≤31。
#include<stdio.h>
int main(){
int year, month, day, sum, leap; //sum-第几天;leap-是否为闰年(1-是; 0-否)
printf("\n请输入年(year), 月(month), 日(day)[空格隔开]:\n");
FILE *fp;
if((fp=fopen("TheDay.in", "r")) != NULL ){
fclose(fp); //存在的话,要先把之前打开的文件关掉
freopen("TheDay.in", "r", stdin);
freopen("TheDay.out", "w", stdout);
}
scanf("%d%d%d", &year, &month, &day);
//***************************************************
//(1)先计算'某月以前' 月份的‘总天数’(不考虑是否闰年)
switch(month){
case 1: sum=0; break;
case 2: sum=31; break; //31 =0 +31
case 3: sum=59; break; //59 =31+28
case 4: sum=90; break; //90 =59+31
case 5: sum=120; break; //120=90+30
case 6: sum=151; break; //151=120+31
case 7: sum=181; break; //181=151+30
case 8: sum=212; break; //212=181+31
case 9: sum=243; break; //243=212+31
case 10: sum=273; break; //273=243+30
case 11: sum=304; break; //304=273+31
case 12: sum=334; break; //334=304+30
default:
printf("data error!");
break;
}
//(2)计算到该月该天的‘天数’
sum = sum + day; /*再加上某天的天数*/
//(3.1)根据年份判断是否闰年, 进行处理....
if( year%400 == 0 || (year%4 == 0 && year%100 != 0) ){ /*判断是不是闰年*/
leap = 1;
}else{
leap = 0;
}
//(3.2)根据【是否闰年?】 + 【是否2月份之后?】确定总天数是否+1
if(leap==1 && month>2){/*如果是闰年且月份大于2,总天数应该加一天*/
sum++;
}
//===================================================
printf("%d\n", sum);//这一天是这一年的第%d天!
return 0;
}
1.2. 年龄计算:ComputeAge.cpp(本题20分) 【题目描述】 有n个人(n为整数)坐在一起,问第n个人多少岁?他说比第n-1个人大diff岁(diff为整数)。问第n-1个人岁数,他说比第n-2个人大diff岁。问第n-2个人,又说比第n-3人大diff岁。依次类推,最后问第一个人,他说是8岁。请问第n个人的年龄是多大?
【程序分析】
利用递归的方法,递归分为回推和递推两个阶段。要想知道第n个人岁数,需知道第n-1人的岁数,依次类推,推到第一人(8岁),再往回推。
【输入】
输入文件ComputeAge.in有1行,包含2个int类型的整数,两个整数之间由一个英文状态下的空格隔开,即输入的变量n和diff。
【输出】
输出文件ComputeAge.out有1行,包含1个int类型的整数(变量age的值),即第n个人的年龄。
【输入输出样例1】
ComputeAge.in ComputeAge.out
5 2 16
【输入输出样例2】
ComputeAge.in ComputeAge.out
100 32 3176
【数据限制】
n≥3,diff≥1。
#include<stdio.h>
int computeAge(int n, int diff) {
//********************************************
int c;
if(n == 1){
c = 8;
}else{
c = computeAge(n-1, diff) + diff;
}
return c;
//============================================
}
int main(){
int n, diff, age; //n-人的总数目; diff-人i和人(i-1)之间的年龄差; age-第n个人的年龄
printf("\n请输入总人数(n), 年龄差(diff)[空格隔开]:\n");
FILE *fp;
if((fp=fopen("ComputeAge.in", "r")) != NULL ){
fclose(fp); //存在的话,要先把之前打开的文件关掉
freopen("ComputeAge.in", "r", stdin);
freopen("ComputeAge.out", "w", stdout);
}
scanf("%d%d", &n, &diff); //输入总人数、年龄差
//********************************************
age = computeAge(n, diff); //调用函数computeAge(n, diff)获得第n人的年龄
//============================================
printf("%d\n", age); //输出第n人的年龄
return 0;
}
1.3. 判断星期几:Week.cpp(本题30分) 【题目描述】 请输入星期几的第一个字母来判断一下是星期几;如果第一个字母一样,则继续输入第二个字母以判断是星期几。输入错误时,请输出字符串信息“data error”;输入正确时,请输出星期几对应的英文单词(monday, tuesday, wednesday, thursday, friday, saturday, Sunday。注意:全小写格式)。
【程序分析】
第1个字母用switch语句进行判断。若第一个字母一样,则用if语句判断第2个字母。
【输入】
输入文件Week.in有1行,包含若干个字符,即输入的若干个描述星期的字符。
【输出】
输出文件Week.out有1行,包含判断结果的输出。输入错误时,输出字符串信息“data error”;输入正确时,输出星期几对应的英文单词(monday, tuesday, wednesday, thursday, friday, saturday, sunday),全小写格式。
【输入输出样例1】
Week.in Week.out
fRiDay friday
【输入输出样例2】
Week.in Week.out
tuDDD tuesday
【输入输出样例3】
Week.in Week.out
988uhk data error
【数据限制】
必须输入至少1个字符。
#include <stdio.h>
#include <ctype.h> /*字符大小写转换函数: char toupper(char)和char tolower(char)*/
int main(){
char letter; //letter-输入的某个字符
printf("请输入星期字母字符:\n"); //请输入星期的第1-2个字母
FILE *fp;
if((fp=fopen("Week.in", "r")) != NULL ){
fclose(fp); //存在的话,要先把之前打开的文件关掉
freopen("Week.in", "r", stdin);
freopen("Week.out", "w", stdout);
}
//************************************************
switch (toupper(letter=getchar())){
case 'S':
//printf("请输入星期的第2个字母:\n");
if(tolower(letter=getchar()) == 'a'){
printf("saturday\n");
}else if (tolower(letter=getchar()) == 'u'){
printf("sunday\n");
}else{
printf("data error\n");
}
break;
case 'F':
printf("friday\n");
break;
case 'M':
printf("monday\n");
break;
case 'T':
//printf("请输入星期的第2个字母:\n");
if(tolower(letter=getchar()) == 'u'){
printf("tuesday\n");
}else if (tolower(letter=getchar()) == 'h'){
printf("thursday\n");
}else{
printf("data error\n");
}
break;
case 'W':
printf("wednesday\n");
break;
default:
printf("data error\n");
}
//================================================
return 0;
}
- 日期与时间——提高题
2.1. 编制万年历:Calendar.cpp(本题100分) 【题目描述】 根据输入的年份,输出该年份的年历(1至12月的月历)。
【题目分析】
目标是学习和掌握三维数组的用法。
函数f(…)和g(…)用于求出该年1月1日的星期。
月份month在程序中的数值为011,分别表示112月。月份值0表示1月。
由闰年的判断条件(四年且不是100年一闰, 400年一闰),确定该年是否为闰年。
为了便于按星期输出各月的月历,引入三维数组的日期表int date[12][6][7]。其中,12表示12各月,6表示一个月最多有6个星期,7表示每星期7天。程序首先将日期表中各元素的值置为0,然后顺序将各月的日期填写到日期表中,各月的天数按月取自数组day_tbl[][](该数组是二维数组,第一行存储平年各月的天数,第二行存储闰年各月的天数)。
【输入】
输入文件Calendar.in有1行,包含1个int类型的正整数,即输入的年份year。
【输出】
程序的输出分两栏。左边一栏自上到下依次是16月的月历,右边一栏自上到下依次是712月的月历。输出时,若日期数为0,就用4个空白符代替,否则输出日期。具体输出格式请查看【输入输出样例】。
程序对应的输出文件Calendar.out为上述输出(有若干行)。
【输入输出样例】
Calendar.in Calendar.out
2016
【数据限制】
year≥1。
/* 输入年份,输出该年的年历. */
#include "stdio.h"
/**使用德国数学家克里斯蒂安·蔡勒公式(Zeller’s Formula)计算给定年份year的第一天是星期几。具体方法:自己上网查询与学习!*/
/*功能: 如果月份<3(三月之前), 则年份回退1年; 否则, 视为当年. */
int YEAR(int year, int month){ /* 如果'月'<3, 则f(年, 月)='年-1'; 否则, f(年, 月)='年' */
if (month < 3){
return year-1;
}else{
return year;
}
}
/*功能: 因为月份<3(三月之前)年份回退1年, 因此月份相应调整. */
int MONTH(int month){ /* 如果'月'<3, g(月)='月+13'; 否则, g(月)='月+1' */
if (month < 3){
return month + 12;
}else{
return month;
}
}
/*功能: 判断某年(year)某月(month)某日(day)是星期几?返回整型int:0~6, 0表示星期天, 1~6表示星期一~六.
方法:使用德国数学家克里斯蒂安·蔡勒(Christian Zeller, 1822-1899)在1886年推导出的,因此通称为蔡勒公式(Zeller’s Formula)
W = [C/4] - 2C + y + [y/4] + [13 * (M+1) / 5] + d - 1.
其中,公式由世纪数减一(C)、年份末两位(y)、月份(M)和日数(d)即可算出W,再除以7,得到的余数是几就表示这一天是星期几。
唯一需要变通的是要把1月和2月当成上一年的13月和14月,C和y都按上一年的年份取值。
因此,人们普遍认为这是计算任意一天是星期几的最好的公式。
注意:
(1)公式都是基于公历(格里高利历)的置闰规则来考虑的!
(2)负数不能按习惯的余数的概念求余数,只能按数论中的余数的定义求余。为了方便计算,我们可以给它加上一个7的整数倍,使它变为一个正数,比如加上70,得到55。再除以7,余6,说明这一天是星期六。这和实际是一致的!
现在仍然让我们来算2004年5月1日的星期,显然C=20,y=4,M=5,d=1,代入蔡勒公式,有:
W = [20/4] - 40 + 4 + 1 + [13 * (5+1) / 5] + 1 - 1
= -15.
*/
int WeekDay(int year, int month, int day){
long trYEAE = YEAR(year, month);
int C = trYEAE/100; //世纪数减一
int y = trYEAE%100; //年份末两位
int M = MONTH(month);
int d = day;
int W = (C/4) - 2*C + y + (y/4) + (13*(M+1)/5) + d -1;
//printf("YEAR=%d, C=%d, y=%d, M=%d, d=%d, W=%d \n", YEAR, C, y, M, d, W);
//根据W的值,判定星期几?
if( W < 0){ //如果W为负,不好求余数, 则加上若干个7, 使之非负
while( W < 0){
W += 7;
}
}
int weekday = W%7; //对7求余数,得到year/month/day这一天是星期几
return weekday;
}
/*功能: 判断给定的年份year是否为闰年? 四年一闰, 100年不闰, 400年一闰。1-是; 0-否。*/
int IsLeap(int year){
return (year%4==0 && year%100) || (year%400 == 0);
}
int date[12][6][7]; //日期表
int day_tbl[][12] = {//365*3 + 366 = 1095 + 366 = 1461
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, //'平'年每月天数(30*4 + 31*7 + 28 = 120+217+28 = 365)
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} //'闰'年每月天数(30*4 + 31*7 + 29 = 120+217+28 = 366)
};
int main(){
int sw, leap, i, j, k, wd, day; //sw-星期几? leap-是否闰年; wd-周; day-一个月的哪一天?
int year; //年
char title[] = " SUN MON TUE WED THU FRI SAT"; //'星期'标题(每个日期占4个字符, 右对齐)
printf("请输入年份(year):"); //年
FILE *fp;
if((fp=fopen("Calendar.in", "r")) != NULL ){
fclose(fp); //存在的话,要先把之前打开的文件关掉
freopen("Calendar.in", "r", stdin);
freopen("Calendar.out", "w", stdout);
}
scanf("%d", &year); //输入年份值及回车
//**************************************************************************************
/*(1)该年(year) 1月1日是星期几? */
sw = WeekDay(year, 1, 1); //返回整型int:0~6, 0表示星期天, 1~6表示星期一~六.
/*(2)判断该年(year)是否是闰年? */
leap = IsLeap(year); //是否闰年(1-是; 0-否)
/*(3)初始化日期表,将日期表中各元素的值置为0 */
for(i=0; i<12; i++){ //0~11 一至十二月
for(j=0; j<6; j++){ //0~5 本月一至五周
for(k=0; k<7; k++){ //0~6 本周 周日至周六
date[i][j][k] = 0; //日期表置0
}
}
}
/*(4)根据给定的年份(year), 赋值日期数据*/
for(i=0; i<12; i++){//一年12个月
/**将第i月(共day_tbl[leap][i]天)的日期(1~day_tbl[leap][i])填入日期表 */
for(wd=0, day=1; day<=day_tbl[leap][i]; day++) {
//A.将第i月第'day'天的信息填写在日期表date[][][]的对应地方
date[i][wd][sw] = day; //第i月, 第wd周,星期sw
//B.星期'几'往后移动
sw = (++sw)%7; //每星期7天, 以0~6计数
//C.碰到0(星期天), 新的一周开始
if(sw == 0){
wd++; //每7天一周, 星期天开始新的一周
}
}
}
//printf("|===========================%d年的日历===========================|\n|", year); //日历头
/*(5)分两栏输出year年的12各月的日历信息(左栏1-6月, 右栏7-12月) */
for(i=0; i<6; i++){
//A.先测算第i月和第i+6月的最大星期数(日期表的第6行是否有日期?)
for(wd=0, k=0; k<7; k++){ //日期表的第6行有日期,则wd != 0
wd += date[i][5][k] + date[i+6][5][k];
}
//B.第i月和第i+6月有几个星期(要显示几行?)
wd = wd?6:5;
//C.输出第i月和第i+6月(月历)的行头信息(注意:右栏第i+6月的"月份标识%2d"之前有3个空格[前后共6个字符位], 其它控制字符之间一个字符' ')
//printf("%2d %s %2d %s |\n|", i+1, title, i+7, title);
printf("%2d %s %2d %s\n", i+1, title, i+7, title);
//D. 从第0周 至 第wd周, 输出日历信息
for(j=0; j<wd; j++){
//(D.1)空过: 月份标识的3[2+1]个字符位
printf(" "); //输出3个空白符
//(D.2)【左栏为第i月】, 右栏为第i+6月
for(k=0; k<7; k++){ //左侧栏,...
if(date[i][j][k]){ //若有日期(非0), 则'按4个字符宽度的格式'输出(i月)该日期
printf("%4d", date[i][j][k]);
}else{ //无日期, 则'按4个字符宽度的格式'输出4个空格
printf("%4c", ' ');
}
}
//(D.3)第i月和第i+6月(月历) 之间,6个空格隔开
printf("%6c", ' '); //输出6个空白符
//(D.4)左栏为第i月,【右栏为第i+6月】
for(k=0; k<7; k++){ //右侧栏,...
if(date[i+6][j][k]){ //若有日期(非0), 则'按4个字符宽度的格式'输出(i+6月)该日期
printf("%4d", date[i+6][j][k]);
}else{ //无日期, 则'按4个字符宽度的格式'输出4个空格
printf("%4c", ' ');
}
}
//(D.5)一行之后,输入换行符
//printf(" |\n|");
printf("\n");
}
}
//puts("==================================================================|");
return 0;
}