杭电ACM HDU 1006 Tick and Tick

题目大意:输入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)); 
  }
} 

 

 

    

转载于:https://www.cnblogs.com/denghui666/p/7622098.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值