“三天打鱼两天晒网”问题的JAVA解法

问题描述

中国有句俗语叫“三天打鱼两天晒网”。某人从2010年1月1日起开始“三天打鱼两天晒网”,问这个人在以后的某一天中是“打鱼”还是“晒网”。用C或C++语言实现程序解决问题。

问题分析加算法设计

本题需要求出此人在某一天是“打鱼”还是“晒网”的状态,因此,需首先求出从2010年1月1日起到所输入日期的时间间隔天数。因为此人打鱼晒网的周期为5天,因此对天数求5的余数,根据所得余数判断他的状态。我们默认他前三天“打鱼”后两天“晒网”,因此,当余数为1、2、3时,可知他在“打鱼”,当余数为4、0时,则表示他在“晒网”。

输入检查

先判断输入字符串长度是否符合规则,然后拆分字符串并转化为int型数据,看是否能够成功,其次,检查所输入年份是否在初始年份之后,最后检查输入的月日是否符合规范。

求时间间隔天数

应考虑闰年因素,即2月为29天的特殊情况,在求解时,在遇到闰年时应该加一天。而闰年的判断条件是:
年份能够被4整除不能被100整除、或仅能够被400整除

存储数据

使用Dates类进行存储日期,分别存储年year,月mouth,日day,并在初始化时进行闰年判断,为下一步计算日期间隔做准备。
计算日期间隔,将该方法封装为Dates的成员方法,考虑到不确定哪个日期在前的情况,在计算前进行日期的比较,将较早的日期作为减数,解决运算中频繁使用abs()方法求绝对值的问题。
日期间隔分为以下几个部分:闰年数量、平年数量和零散天数(即计算输入日期中的月日为该年中的第几天),最后日期间隔的计算公式即:
总天数 = 闰年数(leapYearNum)*366 + 平年数(commonYearNum)*365 + 零散天数(daysNum)

Utils工具类

闰年数计算、平年数计算、零散日期计算等方法封装在Utils工具类中作为静态方法,方便调用。通过传入两个Dates对象,计算所需要的值。

流程图

运行流程图

代码块

Dates.java

package pm01;

/**
 * @description 记录日期数据,具有计算日期间隔天数的方法
 * @see #year 年份
 * @see #month 月份
 * @see #day 天数
 * @see #getDifference(Dates) 计算两个日期间的时间间隔
 * @author Wang Rongqiang
 * @date 2021-03-20 15:53
 * @version 1.0
 **/

public class Dates {
    //记录日期的年份,月份,日期以及是否为闰年
    private final int year;
    private final int month;
    private final int day;
    private final boolean isLeapY;

    public Dates(String dt) {
        //根据输入字符串中年份月份等数据所在位置,截取出数值
        this.year = Integer.parseInt(dt.substring(0, 4));
        this.month = Integer.parseInt(dt.substring(4, 6));
        this.day = Integer.parseInt(dt.substring(6));
        //判断是否为闰年
        isLeapY = Utils.isLeap(year);
    }

    /**
     *
     * @params [d]
     * @author Wang Rongqiang
     * @Description //TODO 计算两个日期相差天数
     * @date 2021/3/20 17:38
     * @return int
            */
    public int getDifference(Dates d) {
        //记录d日期的年份、月份、日期
        int year = d.getYear();
        int month = d.getMonth();
        int day = d.getDay();

        //记录两年间闰年和平年的数量
        int leapYearNum, commonYearNum;

        //计算两个年间闰年个数
        leapYearNum = Utils.leapYearCount(this,  d);
        //两年间平年个数,总年数减去闰年数
        commonYearNum = Utils.commonYearCount(this, d,
                leapYearNum);

        //计算到一年的零散天数,即month月day天为year年第几天
        int daysNum;
        //判断哪个年份为结束年份,并计算该年零散天数
        if(this.year <= year)
            daysNum = Utils.dayNumInYear(d);
        else
            daysNum = Utils.dayNumInYear(this);

        //计算总天数,闰年*366+平年*365+零散日期
        return leapYearNum *365 + commonYearNum * 366 +
                daysNum;
    }

    public int getYear() {
        return year;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    public boolean isLeapY() {
        return isLeapY;
    }

    @Override
    public String toString() {
        return year + "年" + month + "月" + day + "日";
    }
}

Utils.java

package pm01;

import java.util.Arrays;

/**
 * @description 工具类,实现输入检查,闰年判断、月份合法检查等操作
 * @see #defaultDate 起始日期
 * @see #inputCheck(String) 输入检查
 * @see #dateIsOK(int, int, int) 判断日期是否合法
 * @see #isLeap(int) 判断是否为闰年
 * @see #leapYearCount(Dates, Dates) 计算闰年数
 * @see #commonYearCount(Dates, Dates, int) 计算平年数
 * @see #dayNumInYear(Dates) 计算这天是这年的第几天
 * @see #dayNumInMonth(int) 计算这个月有多少天
 * @author Wang Rongqiang
 * @date 2021-03-20 17:06
 * @version 1.0
 **/

public class Utils {
    public static String defaultDate = "20100101";
    /**
     *
     * @params [input]
     * @author Wang Rongqiang
     * @Description //TODO 判断输入是否合法,日期格式以及日期是否在起始日期后
     * @date 2021/3/20 17:37
     * @return boolean
            */
    public static boolean inputCheck(String input) {
        //先根据长度判断是否合法
        if(input.length()!=defaultDate.length()) {
            System.out.println("输入格式有误!");
            return false;
        }

        //用以记录input的年月日数据
        int y, m, d;
        //先尝试拆分字符串,获取年月日的数据,如果报错则输入非法
        try{
            y = Integer.parseInt(input.substring(0, 4));
            m = Integer.parseInt(input.substring(4, 6));
            d = Integer.parseInt(input.substring(6));
        } catch (NumberFormatException e) {
            System.out.println("输入的格式有误!");
            return false;
        }
        //判断月份和日期是否合法
        if (!dateIsOK(y, m, d)) {
            System.out.println("输入的格式有误!");
            return false;
        }
        //判断输入日期是否在起始日期之前
        if (input.compareTo(defaultDate) < 0) {
            System.out.println("输入日期在初始日期之前!");
            return false;
        }
        return true;
    }

    /**
     *
     * @params [y, m, d]
     * @author Wang Rongqiang
     * @Description //TODO 判断日期是否合法
     * @date 2021/3/20 17:37
     * @return boolean
            */
    private static boolean dateIsOK(int y, int m, int d){
        //月份不会大于12
        if(m > 13)
            return false;
        //记录大月(31天)的月份
        int[] bigMonth = {1, 3, 5, 7, 8, 10, 12};
        //根据月份大小,判断日期是否合法
        //m月是否出现在数组中,判断m月是否为大月
        if(Arrays.binarySearch(bigMonth, m) > 0)
            //m月为大,日期不能大于31
            return d <= 31;
        //m月小,若m不为2月,则d不大于30
        else if(m != 2)
            return d <= 30;
        //m为2月,判断该年是否为闰年
        else if(isLeap(y))
            //闰年2月不能大于29天
            return d <= 29;
        else
            //平年2月不能大于28天
            return d <= 28;
    }

    /**
     *
     * @params [y]
     * @author Wang Rongqiang
     * @Description //TODO 判断年份是否为闰年
     * @date 2021/3/20 17:49
     * @return boolean
            */
    public static boolean isLeap(int y) {
        //根据闰年判断条件书写语句
        return y % 4 == 0 && y % 100 != 0 || y % 400 == 0;
    }

    /**
     *
     * @params [date1, date2]
     * @author Wang Rongqiang
     * @Description //TODO 计算begin和end两日期之间的闰年数
     * @date 2021/3/20 18:08
     * @return int
            */
    public static int leapYearCount(Dates dates1, Dates dates2) {
        //记录起始与结束年份
        int beginYear, endYear;
        //date1,date2两个年份哪一年更早设置起始与结束年份
        if (dates1.getYear() <= dates2.getYear()) {
            beginYear = dates1.getYear();
            endYear = dates2.getYear();
        } else {
            beginYear = dates2.getYear();
            endYear = dates1.getYear();
        }
        //闰年数量
        int count = 0;

        //遍历查找闰年
        for (int i = beginYear; i < endYear; i++) {
            //判断是否为闰年
            if (isLeap(i))
                count++;
        }

        return count;
    }

    /**
     *
     * @params [date1, date2, leapYNum]
     * @author Wang Rongqiang
     * @Description //TODO 计算两年中平年的个数
     * @date 2021/3/20 19:07
     * @return int
            */
    public static int commonYearCount(Dates dates1, Dates dates2, int leapYNum) {
        //记录平年数量
        int commonYearNum;
        //判断哪一年在前,则设置该年为减数,来计算总年数
        //总年数减去闰年数即平年数
        if (dates1.getYear() <= dates2.getYear()) {
            commonYearNum = dates2.getYear() - dates1.getYear() - leapYNum;
        } else {
            commonYearNum = dates1.getYear() - dates2.getYear() - leapYNum;
        }

        return commonYearNum;
    }

    /**
     *
     * @params [dates]
     * @author Wang Rongqiang
     * @Description //TODO 计算某天是这年的第几天
     * @date 2021/3/20 19:30
     * @return int
            */
    public static int dayNumInYear(Dates dates) {
        //记录总天数,此时2月先设为28天
        int dayNum = 0;
        for (int i = 1; i <= dates.getMonth(); i++) {
            if (i == dates.getMonth())
                dayNum+=dates.getDay();
            else
                dayNum+=dayNumInMonth(i);
        }

        //判断是否为闰年且月份大与2,闰年天数加一
        if (dates.getMonth() > 2 && dates.isLeapY())
            dayNum++;

        return dayNum;
    }

    /**
     *
     * @params [m]
     * @author Wang Rongqiang
     * @Description //TODO 计算某月有多少天
     * @date 2021/3/20 19:41
     * @return int
            */
    private static int dayNumInMonth(int m) {
        //将大月记录在数组中
        int[] bigMonth = {1, 3, 5, 7, 8, 10, 12};

        //m月是否出现在数组中,判断m月是否为大月
        if(Arrays.binarySearch(bigMonth, m) > 0)
            return 31;
        //不是2月也不是大月
        else if (m != 2)
            return 30;
        //是2月先返回28天,后续根据平闰年觉得是否加一
        else
            return 28;
    }
}

MainClass.java

package pm01;

import java.io.*;
import java.util.Scanner;

/**
 * @author Wang Rongqiang
 * @version 1.0
 * @description 用来运行程序,并计算这天是打鱼还是晒网
 * @date 2021-03-20 19:47
 **/

public class MainClass {
    public static void main(String[] args) {
        System.out.println("某人从2010年1月1日开始“三天打鱼两天晒网”," +
                "输入一个日期,将获得该天他的状态");
        Scanner sc = new Scanner(System.in);
        int n;
        while (true){
            System.out.println("1、从键盘手动输入");
            System.out.println("2、从文件获取输入");
            System.out.println("3、退出");
            System.out.println("请选择:");
            n = sc.nextInt();
            switch (n) {
                case 1 -> {
                    fromKB();
                }
                case 2 -> {
                    fromFile();
                }
                case 3 -> {
                    System.out.println("退出");
                    return;
                }
            }
        }
    }

    /**
     * 
     * @params []
     * @author Wang Rongqiang 
     * @Description //TODO 从键盘获取输入数据
     * @date 2021/3/21 17:28
     * @return void
            */
    private static void fromKB() {
        Scanner sc = new Scanner(System.in);
        //记录输入的日期
        String date;
        //通过循环,检查输入是否合法,不合法则重新输入
        do {
            System.out.println("请输入一个日期(如:20200524):");
            date = sc.next();
        }while (!Utils.inputCheck(date));

        //使用Dates类记录初始时间和输入时间
        Dates beginDate = new Dates(Utils.defaultDate);
        Dates endDate = new Dates(date);

        //记录日期间隔天数
        int days = beginDate.getDifference(endDate);

        //计算这天是打鱼还是晒网
        //“三天打鱼两天晒网”则应对天数求5的余数
        int n = days % 5;
        System.out.println("此人从" + beginDate.toString() +
                "开始“三天打鱼两天晒网”");
        //判断余数为1,2,3时为打鱼,其余时为晒网
        if (n != 0 && n <= 3) {
            System.out.println("则" + endDate.toString() + "他在“打鱼”");
        } else {
            System.out.println("则" + endDate.toString() + "他在“晒网”");
        }
    }

    /**
     * 
     * @params []
     * @author Wang Rongqiang 
     * @Description //TODO 测试模式,即从文件中批量读取数据
     * @date 2021/3/21 17:29
     * @return void
            */
    private static void fromFile() {
        //测试代码
        //输出文件及地址
        String outPath = "D:/out.txt";
        //输入文件地址
        String inPath = "D:/in.txt";
        File write = new File(outPath);
        try {
            write.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            FileWriter writer = new FileWriter(write);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try (FileReader file = new FileReader(inPath);
             BufferedReader br = new BufferedReader(file);
             ) {
            //记录输入的日期
            String date = "";
            //通过循环,测试多组样例
            do {
                try {
                    date = br.readLine();
                    if (date == null)
                        break;
                    if (!Utils.inputCheck(date))
                        continue;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //使用Dates类记录初始时间和输入时间
                Dates beginDate = new Dates(Utils.defaultDate);
                Dates endDate = new Dates(date);

                //记录日期间隔天数
                int days = beginDate.getDifference(endDate);

                //计算这天是打鱼还是晒网
                //“三天打鱼两天晒网”则应对天数求5的余数
                int n = days % 5;

                try {
                    File writeName = new File(outPath);
                    writeName.createNewFile();
                    FileWriter writer = new FileWriter(writeName, true);
                    BufferedWriter out = new BufferedWriter(writer);
                    //判断余数为1,2,3时为打鱼,其余时为晒网
                    if (n != 0 && n <= 3) {
                    out.write(endDate.toString() + "他在“打鱼”\n");
                    out.flush();
                    } else {
                    out.write(endDate.toString() + "他在“晒网”\n");
                    out.flush();
                    }
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } while (true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

运行结果

  • 通过文件读取批量数据的运行结果

通过文件读取数据的运行情况

  • 通过手动输入的运行结果
    通过手动输入后得到的运行结果
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值