灰灰考研机试班 | 基础练习二 问题 B: 日历本 | 入门题-模拟 | 日期、星期相关计算

27 篇文章 1 订阅

问题 B: 日历本

时间限制: 1 Sec  内存限制: 32 MB

题目描述

我们经常需要使用日历,所以需要一个能生成日历的程序。
先要求你写一个程序,只需要输入年份,就能生成正确的日历。

输入

输入包含多组测试数据。每组输入一个整数Y(1800<=Y<=2100),表示公元年份。

输出

对于每组输入,输出对应的日立本。选中下面的输出样例查看具体的输出格式,注意空格的输出。

样例输入 Copy

2010

样例输出 Copy

(注:不包括行号)

                              2010                              

      January               February               March        
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                1  2      1  2  3  4  5  6      1  2  3  4  5  6
 3  4  5  6  7  8  9   7  8  9 10 11 12 13   7  8  9 10 11 12 13
10 11 12 13 14 15 16  14 15 16 17 18 19 20  14 15 16 17 18 19 20
17 18 19 20 21 22 23  21 22 23 24 25 26 27  21 22 23 24 25 26 27
24 25 26 27 28 29 30  28                    28 29 30 31         
31                                                              
       April                  May                   June        
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
             1  2  3                     1         1  2  3  4  5
 4  5  6  7  8  9 10   2  3  4  5  6  7  8   6  7  8  9 10 11 12
11 12 13 14 15 16 17   9 10 11 12 13 14 15  13 14 15 16 17 18 19
18 19 20 21 22 23 24  16 17 18 19 20 21 22  20 21 22 23 24 25 26
25 26 27 28 29 30     23 24 25 26 27 28 29  27 28 29 30         
                      30 31                                     
        July                 August              September      
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
             1  2  3   1  2  3  4  5  6  7            1  2  3  4
 4  5  6  7  8  9 10   8  9 10 11 12 13 14   5  6  7  8  9 10 11
11 12 13 14 15 16 17  15 16 17 18 19 20 21  12 13 14 15 16 17 18
18 19 20 21 22 23 24  22 23 24 25 26 27 28  19 20 21 22 23 24 25
25 26 27 28 29 30 31  29 30 31              26 27 28 29 30      
                                                                
      October               November              December      
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                1  2      1  2  3  4  5  6            1  2  3  4
 3  4  5  6  7  8  9   7  8  9 10 11 12 13   5  6  7  8  9 10 11
10 11 12 13 14 15 16  14 15 16 17 18 19 20  12 13 14 15 16 17 18
17 18 19 20 21 22 23  21 22 23 24 25 26 27  19 20 21 22 23 24 25
24 25 26 27 28 29 30  28 29 30              26 27 28 29 30 31   

笔记

这就是个纯模拟的题,但看起来似乎很麻烦,且无从下手。

我们不妨从样例输出上入手,可发现大概可以将一张日历划分为title和四个line,每个line包含三个block:

而每个block又可分为三个部分:block_title、week_name、day。

可以看到,day的部分固定占六行,每一个日期右对齐并占两位,两个日期之间间隔一个空格,两个block之间间隔两个空格。

每月的首日前和最后一日后的日子要用空格填充。

由此,我们的任务就转化为如何依次输出这三部分了。注意,由于控制台的输出特性,每行block的输出是同时进行的。

可用三重循环来实现,一重循环控制line,一重循环控制block,一重循环控制一行day。

title、block_title、week_name

这三部分打印的内容是固定的,因此可以封装为函数直接调用。

有人会说每个line的block_title不同,其实这里传入一个行数参数即可进行统一控制:

void print_block_title(int i){
	switch(i){
		case 1:printf("      January               February               March        \n");break;
		case 2:printf("       April                  May                   June        \n");break;
		case 3:printf("        July                 August              September      \n");break;
		case 4:printf("      October               November              December      \n");break;		
	}
}

day

这块是整道题中最不好打印的地方了。

步骤如下:

  1. 计算每个月首日是星期几,记为w[i];
  2. 月首之前空出的日子用空格填充;
  3. 一次输出同一line中的每一行,共打印6行(分别输出block1、block2、block3中的第一行,再输出第二行,以此类推)。
  4. 月末之后空出的日子用空格填充。

使用数组d[i]跟踪记录输出时每个block中的最新日期,若输出日期值未超过当前月的最大天数,则输出并增加1;否则输出空格。

使用数组w[i]记录每个block对应月份首日的星期,对应关系为:{周日:0,周一:1,周二:2,周三:3,周四:4,周五:5,周六:6}。

使用数组m[i]记录一个line中各个block对应的月份。

计算月首星期的方法

使用蔡勒公式(Zeller formula)来计算每个月第一天是星期几。(注:蔡勒公式只能计算1582年后的日期)

公式如下:

其中:

w表示星期,取值范围为w={0,1,2,3,4,5,6},0为星期日;

c表示世纪,y表示年分,d表示日;

m表示月份,取值范围为[3,14]内的整数。若被求月份为1月和2月,要分别换算为上一年的13月和14月

例如有日期2010-01-05,则c=20,y=10,m=13,d=5。

另外,符号“[ ]”表示向下取整。

在利用该公式代值计算时,要特别注意计算中出现负数的情况,建议按照以下步骤进行计算:

先计算

若w>=0,w = w%7;

否则,w = (w%7+7)%7。

月首和月末日期

每个月第一天之前和最后一天之后的日期要用空格补全。

找规律可知,第一天之前共有w[i]天,最后一天后有7-(w[i]+days[m[i]])%7天,其中days[i]表示第i月的天数,其它数组含义同上所述。

代码

这里的代码是我自己写的(已AC),不是标程

#include<stdio.h> 

int days[13]={-1,31,-1,31,30,31,30,31,31,30,31,30,31};//每月的天数 

void print_block_title(int i){
	switch(i){
		case 1:printf("      January               February               March        \n");break;
		case 2:printf("       April                  May                   June        \n");break;
		case 3:printf("        July                 August              September      \n");break;
		case 4:printf("      October               November              December      \n");break;		
	}
}
void print_week_name(){
	printf("Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa\n");
}
void print_title(int y){
	printf("                              %d                              \n\n",y);
}
/**
判断是否闰年 
**/ 
int is_leap_year(int y){
	if(y%4==0 && y%100!=0 || y%400==0){
		return 1;
	}else{
		return 0;
	}
}

int max_r(int a, int b,int c){
	if(a>=b && a>=c){
		return a;
	}
	if(b>=a && b>=c){
		return b;
	}
	if(c>=a && c>=b){
		return a;
	}
}

/**
计算某个月的第一天是星期几
return:星期(0:星期一,以此类推) 

note:使用蔡勒公式。1,2月要当成上一年的13,14月计算。 
**/
int day_of_first(int y,int m){
	if(m==1 || m==2){
		y--;
		m += 12;
	}
	int c = y/100;
	y %=100;
	int w = y+y/4+c/4-2*c+(26*(m+1)/10);
	if(w<0){
		w = (w%7+7)%7;
	}else{
		w %= 7;
	}
	return w;
}
int main(){
	int Y;
	while(scanf("%d",&Y)!=EOF){		
		
		//title
		print_title(Y);
		
		//block
		int line_i,j;
		int w[3];	//每个月的第一天是星期几(0:星期日)
		int d[3];	//累计日期 
		int m[3];	//月份 
		for(line_i=1;line_i<=4;line_i++){	//循环打印每一行 			
			//初始化2月天数
			if(is_leap_year(Y)) {
				days[2] = 29;
			}else{
				days[2] = 28;
			}
			
			//初始化w、d、m数组
			int i;
			for(i=0;i<3;i++) {
				d[i] = 1;
				m[i] = (line_i-1)*3+i+1;
				w[i] = (day_of_first(Y,m[i]))%7;
			}
			
			//计算行数:不用算了,都是6行 
//			int r1 =  ((7-(w[0]+days[m[0]])%7)+w[0]+days[m[0]])/7;
//			int r2 =  ((7-(w[1]+days[m[1]])%7)+w[1]+days[m[1]])/7;
//			int r3 =  ((7-(w[2]+days[m[2]])%7)+w[2]+days[m[2]])/7;
//			int rows = max_r(r1,r2,r3);				
			
			print_block_title(line_i);
			print_week_name();			
			
			int block_i,row_i;
			for(row_i=0;row_i<6;row_i++){
				for(block_i=0;block_i<3;block_i++){
					if(block_i>0){
						printf("  ");
					}
					
					for(j=0;j<7;j++){
						if(j>0){
							printf(" ");
						}
						if(row_i==0 && j<w[block_i]){//填充空格 :只有第一行才需要填充空格 
							printf("  ");
						}else if(row_i>0 || row_i==0 && j>=w[block_i]){
							if(d[block_i]<=days[m[block_i]]){
								printf("%2d",d[block_i]);
								d[block_i]++;
							}else{
								printf("  ");
							}
						}
					}
				}
				
				printf("\n") ;
			}
		}
	}
	
	return 0;
}

 

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值