CF832C

题目链接:http://codeforces.com/contest/832/problem/C

题目大意:

  n个人,面向左或者右站在同一条轴上,每个人在轴上的坐标为x,速度为v。请你在某个位置放置一个炸弹,炸弹一炸,每个人都会立即朝前跑。炸弹会发出一道怪光,速度s,如果人被这道怪光从身后追上,那么这个人的速度就会 s+v 。请你合理放置炸弹,使得从炸弹爆炸到有人跑到0并且有人跑到1e6所需的时间最短。

解题思路:

  比赛时这道题想了一个多小时,完全没有思路,思路来源于Tutorial

  从0到1e6二分时间,判断在这个时间是否可以达成目标,具体细节请看代码和注释,因为这一题的细节有一点多。

AC代码:

 1 #include <algorithm>
 2 #include <cstdio>
 3 #include <cmath>
 4 using namespace std;
 5 const int maxn=1e5+5;
 6 double s;
 7 int n;
 8 struct p{
 9     double x,v;
10     int t;//t,1左;2右.
11 }man[maxn];
12 bool jiao(pair<double,double>l,pair<double,double>r){
13 //这里是边界情况讨论,非常恶心,其实就是说:因为这里讨论的坐标点都是整数,所以对于一个区间的左端点,我们要向右取整,对于右端点则要向左取整,这样最稳妥。
14     double ls=floor(l.second);
15     double lf=ceil(l.first);
16     double rs=floor(r.second);
17     double rf=ceil(r.first);
18     if(ls>=rf&&lf<=rs)    return true;
19     if(rs>=lf&&rf<=ls)   return true;
20     return false;
21 }
22 bool can(double time){          //判断函数
23 //********************************************
24 //一开始先假设用这么多时间无法到达左右端点
25     bool left=false,right=false;
26     pair<double,double>l,r;     //l和r记录如果要到达左右端,炸弹可以放置的区间
27     l=r=make_pair(1e6*1.0,0.0); //这个区间就代表无法到达左右端点
28 //*****************************************
29     for(int i=0;i<n;i++){
30         double xx=man[i].x,vv=man[i].v;
31 //接下来这部分只分析到达左端点这一部分,到达右端点的同理
32 //事实上,我在打这一部分的时候是左右端判断镜像进行的,这样比较不会乱
33         if(man[i].t==1){
34             if(vv*time>=xx){    //如果光靠这个人自己走就能在这个时间内到达左端点,那么炸弹放在哪里就无所谓了
35                 left=true;
36                 l=make_pair(0.0,1e6*1.0);
37             }
38             else if((vv+s)*time>=xx){   //这是最优的情况,炸弹直接放在这个人脚下,一炸他马上就被加速
39                 left=true;
40                 double t=(time*vv+time*s-xx)/s; //(1)
41                 double maxr=xx-vv*t+s*t;        //(2)
42 //式子(1)、(2)其实就是求炸弹最右能放到哪里,不难想到,当炸弹放在最右,这个人刚好能在规定的时间到达左端
43                 l.first=min(l.first,xx);
44                 l.second=max(l.second,maxr);//更新区间
45             }
46         }
47         else{
48             if(vv*time>=1e6*1.0-xx){
49                 right=true;
50                 r=make_pair(0.0,1e6*1.0);
51             }
52             else if((vv+s)*time>=1e6*1.0-xx){
53                 right=true;
54                 double t=(s*time+vv*time-1e6*1.0+xx)/s;
55                 double maxl=xx+vv*t-s*t;
56                 r.first=min(r.first,maxl);
57                 r.second=max(r.second,xx);
58             }
59         }
60         if(left&&right&&jiao(l,r)){//能到达左右端并且l和r有交集,证明这个时间可以到达
61             return true;
62         }
63     }
64     return false;
65 }
66 int main(){
67     scanf("%d%lf",&n,&s);
68     for(int i=0;i<n;i++)
69         scanf("%lf%lf%d",&man[i].x,&man[i].v,&man[i].t);
70     double l=0.0,r=1e6*1.0;
71     for(int i=0;i<100;i++){     //循环100次就足够精确了
72         double m=(l+r)/2.0;
73         if(can(m))  r=m;
74         else    l=m;
75     }
76     printf("%.7lf\n",l);
77     return 0;
78 }

 

 

  

posted @ 2017-08-12 00:29  Blogggggg  阅读(966)  评论(0编辑  收藏  举报