C语言:两天打鱼,三天晒网

1. 问题描述

中国有一句俗语叫"三天打鱼,两天晒网"。某渔民从1990年1月1日起开始执行"三天打鱼,两天晒网"的计划。问:未来的某一天(从键盘输入),该渔民是在打鱼还是在晒网?

计划规则:

· 周期为5天:打鱼3天,晒网2天

· 周期循环:打鱼(3天) → 晒网(2天) → 打鱼(3天) → 晒网(2天) → ...

2. 学习目标

通过本练习,你将掌握:

· 日期间隔的计算方法

· 闰年的判断

· 年月日的处理技巧

· 周期问题的解决方法

· 输入数据的有效性验证

3. 算法思路

3.1 核心逻辑

要判断某天是打鱼还是晒网,需要:

  1. 计算从1990年1月1日到目标日期经过的总天数

  2. 用总天数对5取余(因为周期为5天)

  3. 根据余数判断:

· 余数为1、2、3:打鱼

· 余数为4、0:晒网

3.2 为什么这样判断?

假设1990年1月1日是周期的第1天(打鱼):

· 第1天:打鱼(余数1)

· 第2天:打鱼(余数2)

· 第3天:打鱼(余数3)

· 第4天:晒网(余数4)

· 第5天:晒网(余数0)

· 第6天:打鱼(余数1)

· 以此类推...

4. 代码实现

4.1 闰年判断函数

复制代码
#include <stdio.h>

#include <stdbool.h>



// 判断是否为闰年

bool isLeapYear(int year) {

    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);

}

4.2 获取某月的天数

复制代码
// 获取某年某月的天数

int getDaysInMonth(int year, int month) {

    int daysInMonth[] = {31, 28, 31, 30, 31, 30, 

                         31, 31, 30, 31, 30, 31};

    

    // 2月特殊处理

    if (month == 2 && isLeapYear(year)) {

        return 29;

    }

    return daysInMonth[month - 1];

}

4.3 计算从1990年1月1日到目标日期的总天数

复制代码
// 计算总天数

int calculateTotalDays(int year, int month, int day) {

    int totalDays = 0;

    

    // 计算年份的天数

    for (int y = 1990; y < year; y++) {

        totalDays += isLeapYear(y) ? 366 : 365;

    }

    

    // 计算月份的天数

    for (int m = 1; m < month; m++) {

        totalDays += getDaysInMonth(year, m);

    }

    

    // 加上当月天数

    totalDays += day;

    

    return totalDays;

}

4.4 判断打鱼还是晒网

复制代码
// 判断状态

const char* judgeFishingOrNetting(int totalDays) {

    int remainder = totalDays % 5;

    

    if (remainder >= 1 && remainder <= 3) {

        return "打鱼";

    } else {

        return "晒网";

    }

}

4.5 主函数

复制代码
int main() {

    int year, month, day;

    

    printf("========================================\n");

    printf(" 三天打鱼,两天晒网 判断程序\n");

    printf("========================================\n");

    printf("起始日期:1990年1月1日\n");

    printf("请输入你要查询的日期(格式:年 月 日):\n");

    

    // 输入日期

    scanf("%d %d %d", &year, &month, &day);

    

    // 输入验证

    if (year < 1990) {

        printf("错误:年份不能小于1990年!\n");

        return 1;

    }

    

    if (month < 1 || month > 12) {

        printf("错误:月份必须在1-12之间!\n");

        return 1;

    }

    

    int maxDays = getDaysInMonth(year, month);

    if (day < 1 || day > maxDays) {

        printf("错误:日期必须在1-%d之间!\n", maxDays);

        return 1;

    }

    

    // 计算总天数

    int totalDays = calculateTotalDays(year, month, day);

    

    // 输出结果

    printf("----------------------------------------\n");

    printf("从1990年1月1日到%d年%d月%d日\n", year, month, day);

    printf("经过的总天数:%d天\n", totalDays);

    printf("周期位置:第%d天(周期5天)\n", totalDays % 5 == 0 ? 5 : totalDays % 5);

    printf("结果:这一天应该【%s】\n", judgeFishingOrNetting(totalDays));

    printf("========================================\n");

    

    return 0;

}

5. 完整代码(优化版)

复制代码
#include <stdio.h>

#include <stdbool.h>



// 判断闰年

bool isLeapYear(int year) {

    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);

}



// 获取某月的天数

int getDaysInMonth(int year, int month) {

    int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    if (month == 2 && isLeapYear(year)) {

        return 29;

    }

    return days[month - 1];

}



// 验证日期是否有效

bool isValidDate(int year, int month, int day) {

    if (year < 1990) return false;

    if (month < 1 || month > 12) return false;

    

    int maxDay = getDaysInMonth(year, month);

    if (day < 1 || day > maxDay) return false;

    

    return true;

}



// 计算从1990-01-01到目标日期的天数

int calculateDays(int year, int month, int day) {

    int total = 0;

    

    // 计算整年的天数

    for (int y = 1990; y < year; y++) {

        total += isLeapYear(y) ? 366 : 365;

    }

    

    // 计算整月的天数

    for (int m = 1; m < month; m++) {

        total += getDaysInMonth(year, m);

    }

    

    // 加上当月天数

    total += day;

    

    return total;

}



// 判断状态

void printResult(int days) {

    printf("经过天数:%d天\n", days);

    

    int remainder = days % 5;

    printf("余数:%d(周期5天)\n", remainder);

    

    if (remainder >= 1 && remainder <= 3) {

        printf("结果:打鱼 💪\n");

    } else {

        printf("结果:晒网 😴\n");

    }

}



int main() {

    int year, month, day;

    

    printf("+--------------------------------------+\n");

    printf("| 三天打鱼,两天晒网 判断程序 |\n");

    printf("+--------------------------------------+\n");

    printf("| 起始日期:1990年1月1日 |\n");

    printf("| 周期规律:打鱼3天 → 晒网2天 |\n");

    printf("+--------------------------------------+\n\n");

    

    // 输入日期

    printf("请输入查询日期(年 月 日):");

    scanf("%d %d %d", &year, &month, &day);

    

    // 验证输入

    if (!isValidDate(year, month, day)) {

        printf("\n错误:无效的日期!\n");

        printf("提示:年份>=1990,月份1-12,日期要符合当月天数\n");

        return 1;

    }

    

    // 计算并输出结果

    printf("\n=== 计算结果 ===\n");

    int days = calculateDays(year, month, day);

    printResult(days);

    

    return 0;

}

6. 运行示例

复制代码
+--------------------------------------+

| 三天打鱼,两天晒网 判断程序 |

+--------------------------------------+

| 起始日期:1990年1月1日 |

| 周期规律:打鱼3天 → 晒网2天 |

+--------------------------------------+



请输入查询日期(年 月 日):2024 3 18



=== 计算结果 ===

经过天数:12508天

余数:3(周期5天)

结果:打鱼 💪

7. 扩展练习

7.1 批量查询

复制代码
void batchQuery() {

    int year, month, startDay, endDay;

    printf("请输入年份和月份,以及查询的日期范围:\n");

    printf("年 月 起始日 结束日:");

    scanf("%d %d %d %d", &year, &month, &startDay, &endDay);

    

    printf("\n%s年%d月 %d日-%d日的状态:\n", year, month, startDay, endDay);

    for (int day = startDay; day <= endDay; day++) {

        if (isValidDate(year, month, day)) {

            int days = calculateDays(year, month, day);

            printf("%2d日:%s\n", day, 

                   (days % 5 >= 1 && days % 5 <= 3) ? "打鱼" : "晒网");

        }

    }

}

7.2 统计功能

复制代码
void statistics(int year, int month) {

    int fishingDays = 0, nettingDays = 0;

    int daysInMonth = getDaysInMonth(year, month);

    

    for (int day = 1; day <= daysInMonth; day++) {

        int totalDays = calculateDays(year, month, day);

        if (totalDays % 5 >= 1 && totalDays % 5 <= 3) {

            fishingDays++;

        } else {

            nettingDays++;

        }

    }

    

    printf("%d年%d月:打鱼%d天,晒网%d天\n", 

           year, month, fishingDays, nettingDays);

}

7.3 自定义起始日期

复制代码
// 修改程序,让用户可以自定义起始日期

int calculateDaysFromStart(int startYear, int startMonth, int startDay,

                          int targetYear, int targetMonth, int targetDay) {

    // 计算从自定义起始日期到目标日期的天数

    // 需要考虑起始日期可能不是1月1日

}

8. 常见问题解答

Q:为什么用余数1-3表示打鱼?

A:因为我们把起始日(1990年1月1日)当作周期的第1天,所以第1、2、3天打鱼,第4、5天晒网。总天数对5取余后,余数1、2、3对应打鱼,余数4和0对应晒网。

Q:如果起始日不是周期的第1天怎么办?

A:可以调整判断逻辑。例如如果起始日是晒网的第1天,那么余数1-2可能是晒网,3-5是打鱼,余数0是打鱼。关键是理解周期和起始点的关系。

Q:为什么要进行输入验证?

A:防止用户输入不合理的日期(如2月30日),确保程序正确运行。这是良好编程习惯的一部分。

Q:如何处理公元前或者未来日期?

A:本程序只处理1990年以后的日期。如果要处理更早的日期,需要考虑历法变化(如格里高利历改革)。未来日期理论上可以用同样的算法,只要年份不超过int范围。

9. 知识点总结

知识点 应用

闰年判断 正确处理2月天数

数组 存储每月天数

循环 计算累计天数

条件判断 输入验证、状态判断

取余运算 周期判断

函数封装 模块化代码

10. 课后作业

  1. 基础版:实现程序,可以查询任意1990年后的日期

  2. 进阶版:添加统计功能,计算某年某月的打鱼/晒网天数

  3. 挑战版:允许用户自定义起始日期和周期规则(如打鱼a天,晒网b天)

  4. 高难度:添加日期范围查询,并生成日历样式的输出

11. 思维拓展

这个程序的核心思想是周期问题,类似的应用有很多:

· 值班排班系统(如5人轮班)

· 交通信号灯控制

· 课程表安排

· 生产计划排程

理解并掌握这种周期问题的解决方法,对解决实际问题很有帮助!

记住:编程不仅仅是写代码,更重要的是理解问题背后的数学逻辑。通过这个练习,你不仅学会了日期计算,更掌握了周期问题的通用解决方法。

相关推荐
sheeta19982 小时前
苍穹外卖Day04笔记
笔记
菜菜小狗的学习笔记2 小时前
剑指Offer算法题(四)链表
数据结构·算法·链表
myloveasuka2 小时前
[Java]查找算法&排序算法
java·算法·排序算法
清水白石0082 小时前
Free-Threaded Python 实战指南:机遇、风险与 PoC 验证方案
java·python·算法
We་ct2 小时前
LeetCode 148. 排序链表:归并排序详解
前端·数据结构·算法·leetcode·链表·typescript·排序算法
本喵是FW3 小时前
C语言手记1
java·c语言·算法
咱就是说不配啊3 小时前
3.19打卡day33
数据结构·c++·算法
2501_924952693 小时前
嵌入式C++电源管理
开发语言·c++·算法
2401_842623653 小时前
C++中的访问者模式高级应用
开发语言·c++·算法