CCF-201712-3-Crontab

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/AivenZhong/article/details/82384502

时间限制: 10.0s

内存限制: 256.0MB

问题描述:

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

样例输入
3 201711170032 201711222352
0 7 * * 1,3-5 get_up
30 23 * * Sat,Sun go_to_bed
15 12,18 * * * have_dinner

样例输出
201711170700 get_up
201711171215 have_dinner
201711171815 have_dinner
201711181215 have_dinner
201711181815 have_dinner
201711182330 go_to_bed
201711191215 have_dinner
201711191815 have_dinner
201711192330 go_to_bed
201711200700 get_up
201711201215 have_dinner
201711201815 have_dinner
201711211215 have_dinner
201711211815 have_dinner
201711220700 get_up
201711221215 have_dinner
201711221815 have_dinner

这道题好恶心,写了260多行写了一天,最后提交才40分,逻辑没错,内存超限了,还有时间复杂度太高了,超出题目要求的10s以内。提交2次,都是11秒。后来在每个模块中测速,发现是时间递增占的时间最多。想想也是,一天24小时一共1440分钟,一年就是525600分钟。题目最大日期是2099年12月。那我就取一半多点,50年,就一共26280000分钟。这个数字太大了,要把每分钟都判断一遍,while里面运行26280000次,还没算while里面的时间复杂度。时间复杂度太大了。

思路
首先得把Crontab数据存在list里面。用每个时间在list里面找匹配的Crontab配置数据,找到就输出任务。时间递增,直到时间等于最终时间就停止。我写的代码主要是时间的递增的模块写的比较繁琐,是分钟一直加,加完再看是否进位。这个模块之后要修改,改成只在Crontab配置出现过的时间里递增寻找,跳过没必要查询的时间。

package CCF三级题;

import java.util.ArrayList;
import java.util.Scanner;

public class CCF_Crontab {
    static int[] months = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        String startDate = input.next();
        String endDate = input.next();
        String curDate = startDate;

        //Crontab的每条配置信息存在列表list中
        ArrayList<CrontabData> list = new ArrayList<>();
        for (int i = 0; i < crontabNum; i++) {
            list.add(new CrontabData(input.next(), input.next(),
                    input.next(), input.next(), input.next(), input.next()));
        }

        //从开始时间开始查看每个时间是否符合Crontab
        while (!curDate.equals(endDate)) {
            int year = new Integer(curDate.substring(0, 4));
            int[] data = new int[5];

            //mins
            data[0] = new Integer(curDate.substring(10, 12));
            //hours
            data[1] = new Integer(curDate.substring(8, 10));
            //dayOfMonth
            data[2] = new Integer(curDate.substring(6, 8));
            //month
            data[3] = new Integer(curDate.substring(4, 6));
            //dayOfWeek
            data[4] = getDayOfWeek(curDate) + 1;

            //把这个时间的数据在Crontab配置信息里找匹配的配置
            for (int i = 0; i < list.size(); i++) {
                //找到就输出时间和任务
                if (matchCrontab(list.get(i), data))
                    System.out.println(curDate + " " + list.get(i).assignment);
            }

            //时间流逝,更新时间
            curDate = tickTock(year, data);

        }
    }

    //匹配Crontab的配置信息
    static boolean matchCrontab(CrontabData ct, int[] data) {
        return ct.eachMins[data[0]] && ct.eachHours[data[1]] && ct.eachDayOfMonth[data[2]] &&
                ct.eachMonth[data[3]] && ct.eachDayOfWeek[data[4]];
    }

    //时间往后走
    static String tickTock(int year, int[] data) {

        //加分,满进位
        boolean ifPlusHour = false;
        //满时进位,分归零
        if (data[0] + 1 == 60) {
            data[0] = 0;
            ifPlusHour = true;
        } else
            data[0]++;

        //加时
        boolean ifPlusDay = false;
        if (ifPlusHour) {
            //满天进位,时归零
            if (data[1] + 1 == 24) {
                data[1] = 0;
                ifPlusDay = true;
            } else
                data[1]++;
        }

        //加日
        boolean ifPlusMon = false;
        if (ifPlusDay) {
            //二月
            //非闰年二月
            if (data[3] == 2 && !isLeapYear(year) && data[2] + 1 == 29) {
                data[2] = 1;
                ifPlusMon = true;
            }
            //闰年二月
            else if (data[3] == 2 && isLeapYear(year) && data[2] + 1 == 30) {
                data[2] = 1;
                ifPlusMon = true;
            }
            //小月
            else if (data[2] + 1 == 31 && ((data[3] == 4 || data[3] == 6 ||
                    data[3] == 9 || data[3] == 11))) {
                data[2] = 1;
                ifPlusMon = true;
            }
            //大月
            else if (data[2] + 1 == 32 && ((data[3] == 1 || data[3] == 3 ||
                    data[3] == 5 || data[3] == 7 || data[3] == 8 ||
                    data[3] == 10 || data[3] == 12))) {
                data[2] = 1;
                ifPlusMon = true;
            }
            //没满年,月自增
            else
                data[2]++;
        }

        //加月,
        boolean ifPlusYear = false;
        if (ifPlusMon) {
            //满年进位,月归一
            if (data[3] + 1 == 13) {
                data[3] = 1;
                ifPlusYear = true;
            } else
                data[3]++;
        }

        //加年
        if (ifPlusYear)
            year++;

        String month = data[3] < 10 ? "0" + data[3] : "" + data[3];
        String dayOfMonth = data[2] < 10 ? "0" + data[2] : "" + data[2];
        String hour = data[1] < 10 ? "0" + data[1] : "" + data[1];
        String min = data[0] < 10 ? "0" + data[0] : "" + data[0];

        return year + "" + month + dayOfMonth + hour + min;
    }

    //判断闰年
    static boolean isLeapYear(int year) {
        return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
    }

    //求某天是星期几,返回的是0-6,对应星期一到星期天
    static int getDayOfWeek(String date) {
        //总天数
        int totalDays = 0;

        //加年
        int year = new Integer(date.substring(0, 4));
        for (int i = 1970; i < year; i++)
            totalDays += isLeapYear(i) ? 366 : 365;

        //加月
        int month = new Integer(date.substring(4, 6));
        for (int i = 0; i < month - 1; i++) {
            totalDays += months[i];
            if (i == 1 && isLeapYear(year))
                totalDays++;
        }
        //加日
        totalDays += new Integer(date.substring(6, 8));
        return (2 + totalDays) % 7;
    }
}

//Crontab的一条配置信息
class CrontabData {
    String mins;
    String hours;
    String dayOfMonth;
    String month;
    String dayOfWeek;
    String assignment;

    //存储每个时间单位的状态,False为未标记,True为标记了的,代表该时间单位有任务标记
    boolean[] eachMins = new boolean[60];
    boolean[] eachHours = new boolean[24];
    boolean[] eachDayOfMonth = new boolean[32];
    boolean[] eachMonth = new boolean[13];
    boolean[] eachDayOfWeek = new boolean[8];

    CrontabData() {}

    CrontabData(String mins, String hours, String dayOfMonth,
                String month, String dayOfWeek, String assignment) {
        this.mins = mins;
        this.hours = hours;
        this.dayOfMonth = dayOfMonth;
        this.month = month;
        this.dayOfWeek = dayOfWeek;
        this.assignment = assignment;
        initialize(eachMins, mins.split(","));
        initialize(eachHours, hours.split(","));
        initialize(eachDayOfMonth, dayOfMonth.split(","));
        initialize(eachMonth, month.split(","));
        initialize(eachDayOfWeek, dayOfWeek.split(","));
    }

    //初始化状态数组,标记该Crontab的某条配置信息对应时间点有任务
    void initialize(boolean[] tab, String[] indexArr) {
        for (int i = 0; i < indexArr.length; i++) {
            String[] arr = indexArr[i].split("-");
            int start, end;

            //只标记一个
            if (arr.length == 1) {
                //星号
                if (arr[0].charAt(0) == '*'){
                    start = 0;
                    end = tab.length - 1;
                }
                //单词或数字
                else {
                    start = wordToNum(arr[0]);
                    end = start;
                }
            }
            //连续标记
            else {
                start = wordToNum(arr[0]);
                end = wordToNum(arr[1]);
            }

            //标记
            for (int j = start; j <= end; j++)
                tab[j] = true;
        }
    }

    //单词转数字
    int wordToNum(String word) {
        String[] monthWord = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
                "Aug", "Sep", "Oct", "Nov", "Dec"};
        String[] dayOfWeekWord = {"", "Mon", "Tue", "Web", "Thu", "Fri", "Sat", "Sun"};

        for (int i = 1; i < monthWord.length; i++) {
            if (word.equals(monthWord[i]))
                return i;
        }

        for (int i = 1; i < dayOfWeekWord.length; i++) {
            if (word.equals(dayOfWeekWord[i]))
                return i;
        }

        return new Integer(word);
    }

}

猜你喜欢

转载自blog.csdn.net/AivenZhong/article/details/82384502
今日推荐