HDU 1006 Tick and Tick
题目大意:
时针,分针和秒针都厌倦了其余两针,只有在与其余两针保持\(n\)度以上的距离才能感到高兴。问一天中,三针都高兴的时间占总时间的百分比。注意时间是连续的。
题解:
先缩短一半的时间:早上的12个小时和下午的12个小时时钟指针情况是一样的。
接着就是了解到每次所有的指针从有重合到再次有重合至多有一段连续的时间符合三针分离度大于n。所以只要枚举每个重合到重合的时间段,然后求出每段中符合条件的时间段即可。
由于枚举的是重合到重合,所以先求出时针和分针,分针和秒针,时针和秒针之间的相对角速度和重合周期。通过相对角速度\(hm\),\(hs\),\(ms\),我们可以求出从重合到分离n度所需的时间\(n/hm\),\(n/hs\),\(n/ms\),以及到再重合前n度所需的时间\((360-n)/hm\),\((360-n)/hs\),\((360-n)/ms\)。每次枚举,\(i\),\(j\),\(k\)表示时针和分针,时针和秒针,分针和秒针各自重合的时间,那么\(p=max(i+n/hm,j+n/hs,k+ms)\)就是最早的三针分离n度的时间;\(q=min(i+(360-n)/hm,j+(360-n)/hs,k+(360-n)/ms)\)就是最晚三针合并到n度的时间,那么时间区间\([p,q]\)就是符合条件的时间段。
一开始超时,看了大佬的代码后发现在枚举中可以省去一些不必要的情况。
#include <iostream>
#include <cstdio>
using namespace std;
const double maxn = 12 * 60 * 60;
double hm, hs, ms, T_hm, T_hs, T_ms, n;
int main() {
double h, m, s; // 角速度
h = 1.0 / 120;
m = 1.0 / 10;
s = 6.0;
hm = m - h; hs = s - h; ms = s - m; // 相对角速度
T_hm = 360 / hm; // 表示时针和分针从重合到再次重合所花的时间
T_hs = 360 / hs; // 表示时针和秒针从重合到再次重合所花的时间
T_ms = 360 / ms; // 表示分针和秒针从重合到再次重合所花的时间
while (~scanf("%lf", &n)) {
if (n < 0) {
break;
}
double time[6], p, q, ans = 0;
// 两针分离到n所需的时间
time[0] = n / hm;
time[1] = n / hs;
time[2] = n / ms;
// 两针合并到n所需的时间
time[3] = (360 - n) / hm;
time[4] = (360 - n) / hs;
time[5] = (360 - n) / ms;
for (double i = 0; i <= maxn; i += T_hm) {
for (double j = 0; j <= maxn; j += T_hs) {
// 排除一些情况
if (j + time[1] > i + time[3]) break;
if (i + time[0] > j + time[4]) continue;
for (double k = 0; k <= maxn; k += T_ms) {
// 排除一些情况
if (k + time[2] > i + time[3] || k + time[2] > j + time[4]) break;
if (i + time[0] > k + time[5] || j + time[1] > k + time[5]) continue;
p = max(max(i + time[0], j + time[1]), k + time[2]);
// 取最大值才能保证全都分离n以上
q = min(min(i + time[3], j + time[4]), k + time[5]);
// 取最小值才能保证全都未合并到n以内
if (q > p) {
ans += q - p;
}
}
}
}
printf("%.3lf\n", 100 * ans / maxn);
}
return 0;
}