题目大意:输入D,当时针,分针,秒针两两的角度都大于或等于D时,则三者都很开心,问一天中,三者都开心的时间占了百分之几?
具体题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1006
解题思路:
一、大致的思路为:
根据条件,由于精度限制,不能对时分秒都使用穷举求解,因此采用对时分进行循环穷举,求出每时每分满足条件的秒数,最后进行求和,
列出时间和角度的不等式组,求解关于时间的不等式,求交集,最后对交集求再求并
二、进一步细化:
1)设未知数:因为我们要求两两之间的角度差,将时间设为 i 时 j 分 m 秒比较容易计算各个指针所经过的角度以方便求角度差
2)列不等式组:以时针和分针的作为举例,时针相对于0时走过的角度-分针相对于0分走过的角度=时针相对于分针的角度差(可正可负),范围应在D~360-D之间
时针分针:D<=|360/12*(i+j/60+m/3600)-360/60*(j+m/60)|<=360-D
时针秒针:D<=|360/12*(i+j/60+m/3600)-360/60*m|<=360-D
分针秒针:D<=|360/60*(j+m/60)-360/60*m|<=360-D
3)求解不等式,对于i,j使用列举,未知数为m,因此可所有不等式解释为D<=|a*x+b|<=360-D;
该绝对值不等式解集为 [min((D-b)/a, (360-D-b)/a) , max((D-b)/a, (360-D-b)/a)] U [min((-D-b)/a, (-360+D-b)/a) , max((-D-b)/a, (-360+D-b)/a)],再与0~60求交集
4)对不等式组求解,即求三个不等式解的交集
5)对于以上得到的交集,再求各个交集的并集即是每分钟里满足条件的时间,之后的步骤详见代码
/*时针分针的角度差:360/12*(i+j/60+m/3600)-360/60*(j+m/60) 时针秒针的角度差:360/12*(i+j/60+m/3600)-360/60*m 分针秒针的角度差:360/60*(j+m/60)-360/60*m */ #include<iostream> #include<cmath> #include <algorithm> using namespace std; struct set { double l;//存储解集的左边界 double h;//存储解集的右边界 }; void solve(set *x,double a,double b,double D) {//求解绝对值不等式:D<=|a*x+b|<=360-D,并将所得解和0~60求交集 double u=(D-b)/a;double v=(360-D-b)/a; x[0].l = max(0.0,min(u,v)); x[0].h = min(60.0,max(u,v)); if(x[0].l > x[0].h )//交集为空 x[0].l=x[0].h=0; u=(-D-b)/a;v=(D-360-b)/a; x[1].l = max(0.0,min(u,v)); x[1].h = min(60.0,max(u,v)); if(x[1].l > x[1].h )//交集为空 x[1].l=x[1].h=0; } set intersection(set x1,set x2,set x3) {//求三者的交集 set y; y.l=max(x1.l,max(x2.l ,x3.l)); y.h=min(x1.h,min(x2.h,x3.h)); if(y.l>y.h ) y.l=y.h=0;//交集为空 return y; } int main() { double D; while(cin>>D&&D!=-1) { double s=0;//记录三者都开心的秒数 set x[3][2],y[8]; /*求解每小时每分钟符合条件的秒数*/ for(int i = 0 ; i < 12 ;i++) { for(int j=0 ; j < 60 ; j++) { double a = 360.0/12*1/3600-360.0/60*1/60;//常数的小数点不能少 double b = 360.0/12*(i+j/60.0)-360.0/60*j; solve(x[0],a,b,D);//时针和分针满足要求的解 a=360.0/12*1/3600-360.0/60; b=360.0/12*(i+j/60.0); solve(x[1],a,b,D);//时针和秒针满足要求的解 a=360.0/60*1/60-360.0/60; b=360.0/60*j; solve(x[2],a,b,D);//分针和秒针满足要求的解 //求交集 int k=0; for(int p=0;p<2;p++) for(int q=0;q<2;q++) for(int r=0;r<2;r++) {//三层循环来表示x[0],x[1],x[2]中各选一个进行组合, //并求其交集 y[k]=intersection(x[0][p],x[1][q],x[2][r]); //因为y[0~7]集合都是不相交的,所以并集可以直接相加(当没有仔细考虑到这些集合是不相交的时,可用接下来的代码2,比较麻烦,但是也有可以借鉴的地方) s+=y[k].h -y[k].l ; k++; } } printf("%.3lf\n",s*100.0/(60*60*12)); } }
#include<iostream> #include<cmath> #include <algorithm> using namespace std; struct set { double l;//存储解集的左边界 double h;//存储解集的右边界 }; void solve(set *x,double a,double b,double D) {//求解绝对值不等式:D<=|a*x+b|<=360-D,并将所得解和0~60求交集 double u=(D-b)/a;double v=(360-D-b)/a; x[0].l = max(0.0,min(u,v)); x[0].h = min(60.0,max(u,v)); if(x[0].l > x[0].h )//交集为空 x[0].l=x[0].h=0; u=(-D-b)/a;v=(D-360-b)/a; x[1].l = max(0.0,min(u,v)); x[1].h = min(60.0,max(u,v)); if(x[1].l > x[1].h )//交集为空 x[1].l=x[1].h=0; } set intersection(set x1,set x2,set x3) {//求三者的交集 set y; y.l=max(x1.l,max(x2.l ,x3.l)); y.h=min(x1.h,min(x2.h,x3.h)); if(y.l>y.h ) y.l=y.h=0;//交集为空 return y; } bool less1(const set &y1, const set &y2) { return y1.l < y2.l; } int main() { double D; while(cin>>D&&D!=-1) { double s=0;//记录三者都开心的秒数 set x[3][2],y[8]; /*求解每小时每分钟符合条件的秒数*/ for(int i = 0 ; i < 12 ;i++) { for(int j=0 ; j < 60 ; j++) { double a = 360.0/12*1/3600-360.0/60*1/60; double b = 360.0/12*(i+j/60.0)-360.0/60*j; solve(x[0],a,b,D);//时针和分针满足要求的解 a=360.0/12*1/3600-360.0/60; b=360.0/12*(i+j/60.0); solve(x[1],a,b,D);//时针和秒针满足要求的解 a=360.0/60*1/60-360.0/60; b=360.0/60*j; solve(x[2],a,b,D);//分针和秒针满足要求的解 //求交集 int k=0; for(int p=0;p<2;p++) for(int q=0;q<2;q++) for(int r=0;r<2;r++) {//三层循环来表示x[0],x[1],x[2]中各选一个进行组合, //并求其交集 y[k]=intersection(x[0][p],x[1][q],x[2][r]); k++; } //求并集 sort(y,y+7,less1);//排序 set c=y[0]; for(int i=1;i<=7;i++) { if(y[i].l<=c.h&&y[i].h>c.h) { c.h=y[i].h ; } else if(y[i].l>c.h) { s+=c.h-c.l; c.l=y[i].l; c.h=y[i].h; } } s+=c.h-c.l; } } printf("%.3lf\n",s*100.0/(60*60*12)); } }