title: HDU-1006
categories:
- ACM
- 盲集合求交
tags: - 临界最优
- 屏蔽细节
date: 2020-02-14 11:10:09
一般我们的思维都是先找第一段符合题目的解,然后找第二段第三段,从而理解题目和找出规律。但对于有些题目,规律甚至第一段的解都非常难找,但如果我们知道解位于一个临界集合中,我们就可以不用去找这个解,转而寻找这个临界集合(因为寻找解非常复杂,寻找临界集合却比较容易),前提是可以把解从临界集合中分离出来(一般是临界集合的最值)。这样我们就可以避开寻找解这个非常复杂的过程,简化题目。
题目
滴答和滴答
*时间限制:2000/1000 MS(Java /其他)内存限制:65536/32768 K(Java /其他)
提交总计:26354接受提交:7208
*
问题描述
时钟的三只指针每秒旋转一次,并且每天相遇很多次。最后,他们对此感到无聊,并且每个人都希望远离其他两个人。如果一只手与其余任何一只手至少有D度,那它就是快乐的。您要计算一天中所有双手都开心的时间。
输入值
输入包含许多测试用例。他们每个人都有一条单行,其实数D在0和120之间(含0和120)。输入以-1的D终止。
输出量
对于每个D,用一行打印一天中所有手牌都高兴的时间百分比,精确到小数点后3位。
样本输入
0
120
90
-1
样本输出
100.000
0.000
6.251
算法
为什么暴力遍历每一秒不行
因为本题对精度有非常高的要求,这个表应该是静音表(扫秒表),不会发出滴答滴答的声音,并且要用到角速度,因为只有这样才能满足精度要求
分析
本题如果仔细思考求解的话,整个过程是非常复杂的,因为你不知道每一小段开始时究竟是那两个针到达临界值。既然这样那就求临界集合,将分针和时针从重合到再次重合看做一个大周期,将分针和表针从重合到再次重合看做一个周期,将时针和秒针从重合到再次重合看做一个小周期。一共有三个针,C32共有三种可能,也就是说每一小段的开始时刻必然在临界开始集合(分-时=n,秒-时=n,秒-分=n)中,并且是临界开始集合的最大值,而每一小段的结束必然存在于临界结束集合(分-时=360-n,秒-时=360-n,秒-分=360-n)中,并且是临界结束集合的最小值。这样只求两个临界集合就行了
代码
#include<iostream>
#include<stdio.h>
using namespace std;
double max(double a,double b,double c){
double max=a>b?a:b;
return max>c?max:c;
}
double min(double a,double b,double c){
double min=a<b?a:b;
return min<c?min:c;
}
int main()
{ freopen("input.txt", "r", stdin);
double n;
while(cin>>n&&n!=-1){
double maxx=12*60*60;
double shij=30.0/60/60;
double fenj=6/60.0;
double miaoj=6/1.0;
double sfx=fenj-shij;
double smx=miaoj-shij;
double fmx=miaoj-fenj;
double sfz=360/sfx;
double sfn=n/sfx;
double sf_n=(360-n)/sfx;
double fmz=360/fmx;
double fmn=n/fmx;
double fm_n=(360-n)/fmx;
double smz=360/smx;
double smn=n/smx;
double sm_n=(360-n)/smx;
double shichang=0;
for(double i=0;i<=maxx;i+=sfz)
{
for(double j=0;j<=maxx;j+=fmz)
{
if(j+fm_n<i+sfn) continue;
if(i+sf_n<j+fmn) break;
for(double k=0;k<=maxx;k+=smz)
{
if(k+sm_n<i+sfn||k+sm_n<j+fmn) continue;
if(j+fm_n<k+smn||i+sf_n<k+smn) break;
double kaishi=max(i+sfn,j+fmn,k+smn);
double jieshu=min(i+sf_n,j+fm_n,k+sm_n);
if(jieshu>kaishi)
shichang+=jieshu-kaishi;
}
}
}
printf("%.3f\n",100.0*shichang/maxx);
}
}