HOJ 1006题 题解

   杭电OJ 1006题 题目链接:点击打开链接

   本题思路:最开始尝试使用枚举的方法,以s为单位进行遍历时发现结果的精度不够,以ms为单位时可以满足精度要求,但是会超时。最终解题思路如下:

(1)秒针1s走6度,分针1s走0.1度,时针1s走1/120度。所以分针秒针每秒的度数差为5.9°,时针分针每秒的度数差为11/120°,时针秒针每秒的度数差为719/120°。

(2)从00:00:00开始考虑,此时分秒针都指向最上方12点方向。12个小时之后,时分秒针再次都指向最上方,完成一个周期,所以只需要计算12小时内三个指针happy的累计“秒数”,然后除以12小时的总秒数就得到了结果(12小时和24小时的结果是一样的~)。

(3)从00:00:00开始考虑,根据上述条件,可以分别计算出时针分针、时针秒针、分针秒针在12小时中,每一次满足happy条件的起止时间,使用数组保存。

(4)需要注意的是,为了满足happy的条件,既需要判断顺时针夹角1是否满足条件,也需要判断该夹角1的对顶角2是否满足条件。因为夹角1和夹角2都是这两个指针构成的角度。

(5)使用数轴来表示时间(使用数轴画一画会更容易理解),根据数组中保存的值寻找交集,则可以找到时针分针秒针都happy的时间段。

  

   本题AC参考代码:

     

import java.io.BufferedInputStream;
import java.text.DecimalFormat;
import java.util.Scanner;

public class Main {

	private static double max(double a, double b, double c) {
		a = a > b ? a : b; // a的值修改为a,b之中较大的那个
		a = a > c ? a : c; // a的值修改为a,c之中较大的那个(参与计算的a已经是a,b中较大的数,所以最终的a是a,b,c中最大的)
		return a;
	}

	private static double min(double a, double b, double c) {
		a = a < b ? a : b;
		a = a < c ? a : c;
		return a;
	}

	public static void main(String[] args) {
		Scanner cin = new Scanner(new BufferedInputStream(System.in));
		int total = 12 * 60 * 60;
		// 记录两两指针之间的起止时间
		// 用于记录分针秒针happy起止时间的数组. bms中的b表示begin,ems中的e表示end
		// 数组大小708,是因为12小时内分针秒针共重合708次,所以happy的起止次数也为708次
		//(重合708次是事先计算好的)
		// 为了防止数组溢出,数组大小设置为709
		double[] bms = new double[709];
		double[] ems = new double[709];

		// 用于记录时针分针happy起止时间的数组.
		// 时针分针12小时内共重合11次
		double[] bhm = new double[12];
		double[] ehm = new double[12];

		// 用于记录时针秒针happy起止时间的数组.
		// 时针分针12小时内共重合719次
		double[] bhs = new double[720];
		double[] ehs = new double[720];

		// 分针秒针每秒相差的度数
		double dms = 5.9;
		// 时针分针每秒的度数差
		double dhm = 11.0 / 120;
		// 时针秒针每秒的度数差
		double dhs = 719.0 / 120;

		double d = cin.nextDouble();

		while (d != -1) {
			// 12小时内,happy时间差累计
			double count = 0;
			
			// 计算并填充各个数组的值
			bms[0] = d / dms; 				// 分针秒针第一次happy的起始时间(顺时针夹角,开始满足条件)
			ems[0] = (360.0 - d) / dms; 	// 分针秒针第一次happy的结束时间(顺时针夹角的对顶角,开始不满足条件)
			for (int i = 1; i < 709; i++) {
				// 分针秒针第i次重合之后,满足happy条件的开始时间
				bms[i] = (360.0 * i) / dms + bms[0]; // (360*i)/dms:为分针秒针第i次重合的时间
				ems[i] = (360.0 * i) / dms + ems[0];
			}
			
			// 计算bhm、ehm、 bhs、 ehs的道理和上述相同
			bhm[0] = d / dhm;
			ehm[0] = (360.0 - d) / dhm;
			for (int i = 1; i < 12; i++) {
				bhm[i] = (360.0 * i) / dhm + bhm[0];
				ehm[i] = (360.0 * i) / dhm + ehm[0];
			}

			bhs[0] = d / dhs;
			ehs[0] = (360.0 - d) / dhs;
			for (int i = 1; i < 720; i++) {
				bhs[i] = (360.0 * i) / dhs + bhs[0];
				ehs[i] = (360.0 * i) / dhs + ehs[0];
			}

			//i、j、k分别用来记录bhs[i], bms[j], bhm[k]数组的当前访问位置
			int i = 0;
			int j = 0;
			int k = 0;
			//时分秒针满足happy条件的开始时间和结束时间
			double begin = 0.0;
			double end = 0.0;
			while (begin <= total && end <= total) {
				// 起始时间取较大值
				begin = max(bhs[i], bms[j], bhm[k]);
				// 结束时间取较小值
				end = min(ehs[i], ems[j], ehm[k]);

				//在一次循环中,只会改变i、j、k中的某一个值,将相对最后的那一个向前移动。。。
				//不太好解释,画画数轴会比较好理解
				if (end == ehs[i]) {
					i++;
				}
				if (end == ems[j]) {
					j++;
				}
				if (end == ehm[k]) {
					k++;
				}
				if (end > begin && end <= total) {
					count += (end - begin);
				}
			}

			// 保留三位小数输出
			DecimalFormat formatter = new DecimalFormat("#0.000");
			double result = ((count + 0.00) / total) * 100;
			System.out.println(formatter.format(result));
			d = cin.nextDouble();
		}
		cin.close();
	}
}

    欢迎评论讨论指正,不喜勿喷。

你可能感兴趣的:(HOJ题解)