Contents

ARST打卡第243周

lc1185_一周中的第几天 TED-无论多大年龄,人生都需要新鲜体验 Protocol Buffer Basics(C++) 荐书-操作系统导论

Algorithm

lc1185_一周中的第几天

思路: 一年52周只占是364天。如果平年就是元旦和年末都是同样的星期,闰年则加一个数值。 这里需要一些背景知识就是1971年1月1日是星期几,但是其实也可以直接用今天是星期六倒退过去。

背景知识1: 1971.1.1是周五

相隔50年的1971年日历和2021年日历重复:在这两年的日历上,日期和星期重合,1月1日都始于星期五,12月31日也都结束于星期五。

1971年1月1日到2021年1月1日,一共经历了18263天,18263正好是7的2609倍,意味着这两个年份的元旦正好相差2609周,即两个元旦在一周当中的位置是一样的。

其次,看两个年份是否都是闰年或平年,也就是我们说的366天和365天。1971年和2021年正好都是平年,这意味着1971年和2021年的每一天都相隔了2609周。这两个年份的日历也就是一样的了。

背景知识2: 闰年

  1. 公元年份非4的倍数,为365天平年。
  2. 公元年份为4的倍数但非100的倍数,为366天闰年。
  3. 公元年份为100的倍数但非400的倍数,(1700年、1800年及1900年)为平年。
  4. 公元年份为400的倍数,(1600年及2000年)为闰年。

100年=36524.22天,按原方案100年本该闰25年,多了0.78天。 所以定出了一个修正方案:100年,需要去掉1个闰年。但仍有偏差,导致每100年少了0.22天。 所以每400年,再加一天,从而减少误差。

具体看 闰年

题解思路

做个题,被背景知识卡了20分钟,小学全忘了…

上面背景知道1971-2100年就只有2000年为闰年,其他为平年,所以可以有如下计算: 1972为闰年,在范围内,除了2100年为平年,其他为闰年。

  1. 通用计算: 鲁棒性最好,直接用 isRun 逐年判断…
  2. 直接取巧通过数据范围计算, 除2100年,其他年都是取模4为0加2,平年加1.

还是选择通用做法吧。这才是出题人想考的…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
    bool isRun(int year) {
        if (year % 400 == 0) {
            return true;
        }
        if (year % 4 == 0) {
            if (year % 100 == 0) {
                return false;
            }
            return true;
        }
        return false;
    }
    string dayOfTheWeek(int day, int month, int year) {
        string ans[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", 
            "Friday", "Saturday"};
        // 以 1971.1.1 周五为起始
        // 先处理以前的年
        // int start = 5; 后面都是计算整年的差值...所以得用 1970.12.31 周四开始
        int start = 4;
        for (int i = 1971; i < year; i++) {
            start++;
            if (isRun(i)) {
                start++;
            }
        }
        start %= 7;
        // 再处理以前的月
        int months[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        for (int m = 1; m < month; m++) {
            start = (start + months[m]) % 7;
        }
        if (isRun(year) && month > 2) {
            start = (start + 1) % 7;
        }
        // 最后处理日
        start = (start + day) % 7;
        return ans[start];
    }
};

题解有缺陷

题解这个方法只对2100年前的数据范围有效,如果更大的话,则会在年份贡献处失效。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
    string dayOfTheWeek(int day, int month, int year) {
        vector<string> week = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
        vector<int> monthDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30};
        /* 输入年份之前的年份的天数贡献 */
        // 这里这个 year - 1969是什么意思
        // 通过 GPT 提示知道其实是 year - 1 - 1968 的意思,
        // 假设1973年,那么就能拿到1972年的奖励...
        int days = 365 * (year - 1971) + (year - 1969) / 4;
        /* 输入年份中,输入月份之前的月份的天数贡献 */
        for (int i = 0; i < month - 1; ++i) {
            days += monthDays[i];
        }
        if ((year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) && month >= 3) {
            days += 1;
        }
        /* 输入月份中的天数贡献 */
        days += day;
        // 因为1970.12.31是周四,所以对应week下标3,加上下标取模
        return week[(days + 3) % 7];
    }
};

Review

TED-无论多大年龄,人生都需要新鲜体验

想到了最强大脑水哥的目标是每天做一件不同的事情。

是啊,人生需要的新体验,如果重复的人生,那将变得非常乏味。

去探索,去发现世界。

Tips

Protocol Buffer Basics(C++)

上面是 protocol buffer 的官方使用示例。

protocol buffer 通过让程序自动化帮我们处理数据的序列化和反序列化, 从而大大减少了重复机械的工作,让开发能专注于接口设计,也更方便服务间解耦,大大降低复杂度。

Share

荐书-操作系统导论

可以通过微信读书阅读,网上也有很多免费资源,因为这本书是公开书,当然可以去作者官网支持作者

这本书从虚拟化,并发,持久化三个切入点讲述了整个操作系统是如何从历史简单的形态一步步发展的。 讲得非常细致,而且来龙去脉很清楚,让人读完有一种通透感,把很多零散的知识连贯起来。

因为本书加深了本人对过往项目经历的理解,所以非常推荐阅读。