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 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”