C/C++编程笔记:C语言编写大学实验课项目——万年历!全解

题目要求

编程实现万年历,要求:

可根据用户输入或系统日期进行初始化,如果用户无输入则显示系统日期所在月份的月历,并突出显示当前日期;

可根据用户输入的日期查询,并显示查询结果所在月份的月历,突出显示当前日期,并提示是否闰年

对任何不合法输入数据,拒绝查询并进行提示。

思路分析

可将思考、编程划分为以下几个模块:

如何通过已有日期和星期推算要求的日期的星期?

如何整齐地输出月历?

如何获取系统时间?

在有余力的前提下,如何美化界面?

下面对上面的几个问题给出粗略的概述。

具体实现和技巧性地东西参考后文代码。

问题1 日期推算

众所周知,需要推算日期的模拟题都是毒瘤题

日期推算的算法有很多,这里只给出我的思路:

推出差了多少天。

用数学公式推出星期。

这条公式是 (w+d)mod7(w+d)mod7 ,d 表示差的天数,w 表示原本是星期几。

我采用的是标准的 0 表示 Sun. 而 6 表示 Sat. 的方法。

time.h 自带的 tm_wday 就是用这种方式表示的。

需要注意的是 C 与 C++ 对负数取模的特(sha)殊(bi)性 ,所以为了求出正确的结果,我们要采用一点小技巧。

if(w1+d<0) w2=(w1+d)+(-w1-d)/7*7+7; 

似乎也可以在推出天数后乘上86400减一下然后扔给 localtime() 去推星期。

但是你连天数都推出来了,直接算不香吗。而且既然是万年历,秒数太大爆了怎么办

接下来让我们考虑如何推算差了多少天。

我为了方便计算,所有的推算都以2020年1月1日星期三为基准。

由一个基准来推的化可以省去很多麻烦。

首先,第一种方法是暴力模拟。一年一年地推、一月一月地推、一天一天地推。

我在代码中注释掉的就是暴力模拟法。

这个没什么好讲的,闰年就差 366 天,否则差 365 天。

年推到了就推月,实现把每个月份的天数打个表,别忘了特判二月就行。

你也可以不像我那样偷懒一个一个月推,使用 前缀和数组+闰年特判 也行。但是每次查询最多就推 12 个月,一个月一个月推也差不了多少。

这点时间肉眼是看不出来的。所以随便吧。

天数就没什么好说的,自己随便想两个同年同月的日期看看差几天,很快就能看出是直接拿日期相减了。

其实,我们不难发现,年份可以不用一年一年模拟,可以用数学公式算。

现在我们要算 A年1月1日 到 B年1月1日 经过了几个闰年。

以 A < B 为例

直接拿 (B-A)/4 来算闰年个数这种玄学的事情我是不会干的。我希望求出的闰年个数是绝对准确的。

因此可以这样来:

我们知道 x/4 可以表示小于等于 x 的正整数中 4 的倍数的个数。

我们需要求经过的闰年的个数,只需要知道区间 [A,B-1] 中 4、100、400 的倍数的个数就行了。

( 因为我考虑的是 1月1日 ,如果考虑 12月31日 的话,应该变为 [A+1,B] )

根据容斥原理,记 4、100、400 的倍数的个数分别为 c1,c2,c3c1,c2,c3

我们有: n=c1−c2+c3n=c1−c2+c3

根据 前缀和 的思想,我们有:

c1=(B−1)/4−(A−1)/4c1=(B−1)/4−(A−1)/4

应该不会有人看不懂前缀和吧,不过我还是解释一下吧。

因为 A 是包含在区间里面的,我们要求 [A,B-1] 的区间权值,自然不能把 A 删出去,所以要用 A-1 。

其它几项同理。

于是我们求出了闰年的个数,于是 d=(B−A)+n×1d=(B−A)+n×1

至于 A > B 的情形,同理,只需要把区间改为 [B,A-1] 。

然后根据前缀和,你会发现 式子是一样的,只是正负号变了而已,所以没有分类讨论的必要 。

这样就解决了最关键的问题,剩下的只需要动用知识和 耐心 去模拟就好了。

问题2 月历的格式

这个随便百度一下万年历或者点一下右下角的时间模仿一下它的格式就行了。这里介绍几个技巧。

分行 printf (这个好像谁都会)

对齐

利用 %-*d 可以

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值