【ZOJ】3326.An Awful Problem

【ZOJ】3326.An Awful Problem

一、题目

原题链接:https://zoj.pintia.cn/problem-sets/91827364500/problems/91827368250

In order to encourage Hiqivenfin to study math, his mother gave him a sweet candy when the day of the month was a prime number. Hiqivenfin was happy with that. But several days later, his mother modified the rule so that he could get a candy only when the day of the month was a prime number and the month was also a prime number. He felt a bit upset because he could get fewer candies. What's worse, his mother changed the rule again and he had to answer a question before he could get a candy in those days. The question was that how many candies he could get in the given time interval. Hiqivenfin wanted to cry and asked you for help. He promised to give you half of a candy if you could help him to solve this problem.

Input

There are multiple test cases. The first line of input is an integer T (0 < T <= 50), indicating the number of test cases. Then T test cases follow. The i-th line of the next T lines contains two dates, the day interval of the question. The format of the date is "yyyy mm dd". You can assume both dates are valid. Hiqivenfin was born at 1000-01-01 and would not die after 2999-12-31, so the queries are all in this interval.

Hiqivenfin didn't seem to be an earthman, but the calendar was the same as that we usually use.
That is to say, you need to identify leap years, where February has 29 days.
In the Gregorian calendar, leap years occur in years exactly divisible by four.
So, 1993, 1994, and 1995 are not leap years, while 1992 and 1996 are leap years.
Additionally, the years ending with 00 are leap years only if they are divisible by 400.
So, 1700, 1800, 1900, 2100, and 2200 are not leap years, while 1600, 2000, and 2400 are leap years.

Output

Output the number of candies Hiqivenfin could get in the time interval. Both sides of the interval are inclusive.

Sample Input

2
1000 01 01 1000 01 31
2000 02 01 2000 03 01

Sample Output

0
10

二、AC代码

思路:算出year1到year2年份中所有的素数月、素数日。再将减去开始年的个数、加上结束年的个数。

注意:如果开始年的当月当天是素数的话,需要加1(2000 2 2 2002 2 2应该输出1)。就因为开始时间这一小点,我的提交WA好几发。。。

import java.util.Scanner;

/**
 * 3326.An Awful Problem
 *
 * 思路:算出year1到year2年份中所有的素数月、素数日。再将减去开始年的个数、加上结束年的个数。
 * 注意:如果开始年的当月当天是素数的话,需要加1(2000 2 2 2002 2 2应该输出1)。就因为开始时间这一小点,我的提交WA好几发。。。
 * @author ChangSheng
 * @date 2020-03-29
 */
public class P3326_AC_An_Awful_Problem {
    static Scanner sc = new Scanner(System.in);
    static int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    static int[] primes = {11, 9, 11, 10, 11, 10, 11, 11, 10, 11, 10, 11};

    public static void main(String[] args) {
        int n = sc.nextInt();
        for (int i = 0; i < n; i++) {
            int[] arr = new int[6];
            for (int j = 0; j < arr.length; j++) {
                arr[j] = sc.nextInt();
            }
            // 这些年中的总个数
            int total = 0;
            for (int j = arr[0]; j < arr[3]; j++) {
                total += total(j);
            }
            // 减去开始年的个数、加上结束年个数
            total -= count(arr[0], arr[1], arr[2]);
            total += count(arr[3], arr[4], arr[5]);
            // 判断开始的时间,是否是素数月、素数日
            if (arr[1] != 1 && arr[2] != 1 && isPreme(arr[1]) && isPreme(arr[2])) {
                total++;
            }
            System.out.println(total);
        }
    }
    /** 计算今年年初到m月d天中,月和天是素数的个数 */
    static int count(int year, int month, int day) {
        if (month < 2) return 0;
        if (month == 2) {
            int days = 0;
            for (int i = 2; i <= day; i++) {
                if (isPreme(i)) {
                    days++;
                }
            }
            return days;
        }
        // month > 2
        int count = 0;
        if (isLeapYear(year)) count = 1;
        for (int i = 2; i < month; i++) {
            if (isPreme(i)) {
                count += primes[i - 1];
            }
        }
        // 最后一个月
        if (isPreme(month)) {
            for (int i = 2; i <= day; i++) {
                if (isPreme(i)) {
                    count++;
                }
            }
        }
        return count;
    }
    /** 计算该年的年初到年末,有多少月份和天都是素数 */
    static int total(int year) {
        int total = 0;
        // 闰年总素数个数+1
        if (isLeapYear(year)) {
            total += 1;
        }
        // 月数天数,同时是素数的个数。
        total += 9 + 11 + 11 + 11 + 10;
        return total;
    }
    /** 闰年判断 */
    static boolean isLeapYear(int year) {
        if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
            return true;
        }
        return false;
    }
    /** 判断素数 */
    static boolean isPreme(int n) {
        boolean flag = true;
        for (int i = 2; i <= Math.sqrt(n); i++) {
            if (n % i == 0) {
                flag = false;
            }
        }
        return flag;
    }
}

三、MLE代码

刚开始,看到这题我非常开心,直接用日期类Calendar,每天+1.妥妥的调用一下Java库不就好了。

后来提交的时候,发现是我太天真了。这题Java用日期类会内存超出限制。。。这里用的素数判断是另一种方法。

附上内存超出的代码:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 3326.An Awful Problem
 * <p>
 * 用日期类Calendar,每天+1,但是超时。。。
 *
 * @author ChangSheng
 * @date 2020-03-29
 */
public class P3326_内存超出_An_Awful_Problem {
    static Scanner sc = new Scanner(System.in);
    static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    static List<Integer> primes = primes();

    public static void main(String[] args) throws ParseException {
        int n = sc.nextInt();
        for (int i = 0; i < n; i++) {
            String dateStr01 = sc.next() + "-" + sc.next() + "-" + sc.next(), dateStr02 = sc.next() + "-" + sc.next() + "-" + sc.next();
            Date d01 = format.parse(dateStr01), d02 = format.parse(dateStr02);
            Calendar c01 = new GregorianCalendar(), c02 = new GregorianCalendar();
            c01.setTime(d01);
            c02.setTime(d02);
            boolean before = true;
            int ans = 0;
            while (before) {
                int month = c01.get(c01.MONTH) + 1, day = c01.get(c01.DAY_OF_MONTH);
                if (primes.contains(month) && primes.contains(day)) {
                    ans++;
                }
                before = c01.before(c02);
                c01.add(Calendar.DAY_OF_MONTH, 1);
            }
            System.out.println(ans);
        }
    }

    public static List<Integer> primes() {
        List<Integer> list = new ArrayList<>(31);
        for (int n = 2; n <= 31; n++) {
            boolean isPrime = true;
            for (int i = 2; i <= Math.sqrt(n); i++) {
                if (n % i == 0) {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime) list.add(n);
        }
        return list;
    }
}