HDU 3400 Line belt
【原题链接】 http://acm.hdu.edu.cn/showproblem.php?pid=3400
【关注一下】http://www.seayar.tk
【题意解释】
这道题的题目意思并不是十分明了,但大致意思如下:
在一个平面,有两条线段(不管是否相交),然后如果在AB线段上移动速度是P,在CD线段上移动速度是Q,在其他地方移动速度是R;最后求解的部分是从点A到点D需要的最小的时间。
就算你知道它的中文意思了,但是你也许还是不太明白要干嘛,可能会想的很复杂,至少我就是那样。所以你得继续往下看,O(∩_∩)O~
【背景介绍】
花了九牛二虎之力,终于解决了这道经典的三分法求解凸函数的题。为了更形象的说明三分法和二分法的区别,下面详细介绍下一些简单函数。
首先是单调函数,就是只单调递增或者单调递减的函数,所以当用二分法解决问题时,如果不在这一边,那肯定就在那一边,一步一步缩小范围,最后逼近要求的解!
而如果碰到了先递增再递减或者是先递减再递增的函数,这个时候二分法的规律就不对了。比如说(以像一座山那样的函数为例),找一个小于x的值,这个时候二分得到中间值,我们可以发现,比它小的值两边都可能出现!这样就不能用二分来解决了,所以今天我们的主角——三分法登上舞台!
【算法介绍】
这是我第一次做三分法,先说下它的核心思想,与二分不同的是,对于在[low,high]这个区间内,不仅要设置一个中分标识mid,你还要设置一个mmid,并且有如下关系:
mid = (low + high) / 2;
mmid = (mid + high) / 2;
是的,下面用个图来表示:
由上图可以看出,f(mid) > f(mmid) ,这说明f(mid)更接近极值点,所以我们要保留[low,mmid]这段区间,从而舍弃后半段,反之可以保留[mid,high],这样就可以像下边那样不断缩小范围:
while(high - low > 设定的某一精度值)
{
if(f(mid) > f(mmid))
{
high = mmid;
mmid = (high + mid) / 2; //更新值
}
else
{
low = mid;
mid = (low + mmid) / 2;
}
}
最后mid值就是我们要求的极值点。还是很简单也很容易理解的吧!
【题目解答】
这道题我们先画个图,然后对题意的理解清楚很多了,如下图:
红线部分就是我们的路径,接下来,我们可以用上三分了。
这道题不仅是经典的三分算法,还是道经典的嵌套三分。首先对AB进行三分,得到两个点,我们可以取其中一个点mid,确定了这个点之后,再对CD进行三分,由于mid点是确定的,那么当我们寻找在CD上某点使得整个时间最小值就是一个凸函数了(具体数学证明比较蛋疼,但是可以抽象理解,有兴趣你可以自己证明下)。在mid确定的前提下,我们就对CD段三分求最小值就行了,然后记录成f(mid),再取AB的mmid点,同样对CD进行三分求值f(mmid),这样和上面说的一样了,就是一个嵌套三分,明白了吗?
下面给出我的AC代码,供大家参考,精度我是设置的比较高,你们要注意点精度的问题,尤其是按照距离来判断循环条件的,看了别人的一些报告,那些精度设置的比较奇怪,所以推荐用时间来判断精度要求。
【参考代码】
1 /************************************************************************/ 2 /* HDU 3400 三分,解题思路见: */ 3 /************************************************************************/ 4 #include <stdio.h> 5 #include <string.h> 6 #include <math.h> 7 #include <stdlib.h> 8 #define EPS 1e-10 9 10 typedef struct Mystruct 11 { 12 double x; 13 double y; 14 }POINT, *point; 15 16 POINT A,B,C,D; 17 double ABspeed,CDspeed,otherspeed; 18 19 double getDistance(POINT start,POINT end) 20 { 21 /************************************************************************/ 22 /* 求两点之间的距离,返回一个double的距离值 */ 23 /************************************************************************/ 24 double distance,truu; 25 distance = sqrt((end.x - start.x)*(end.x - start.x) + (end.y - start.y)*(end.y - start.y)); 26 return distance; 27 } 28 29 void getThreePoint(POINT start,POINT end,point first,point second) 30 { 31 //分别获取三分点 32 /************************************************************************/ 33 /* 参数说明:前两个传入两个坐标点,后两个传入两个三分点的指针 */ 34 /************************************************************************/ 35 first->x = (start.x + end.x)/2; 36 first->y = (start.y + end.y)/2; 37 second->x = (first->x + end.x)/2; 38 second->y = (first->y + end.y)/2; 39 } 40 41 double getBestThreePoint(POINT start,POINT end,POINT pre) 42 { 43 POINT first,second; 44 double t_first = 1,t_second = 0; 45 getThreePoint(start,end,&first,&second); 46 while(fabs(t_first-t_second) > EPS) 47 { 48 double t_have = getDistance(A,pre)/ABspeed; 49 t_first = t_have + getDistance(pre,first)/otherspeed 50 + getDistance(first,D)/CDspeed; 51 t_second = t_have + getDistance(pre,second)/otherspeed 52 + getDistance(second,D)/CDspeed; 53 if(t_first > t_second) 54 { 55 start = first; 56 getThreePoint(start,end,&first,&second); 57 } 58 else 59 { 60 end = second; 61 getThreePoint(start,end,&first,&second); 62 } 63 } 64 return t_first; 65 } 66 67 int main() 68 { 69 #ifndef ONLINE_JUDGE 70 freopen("G:\\input.txt","r",stdin); 71 #endif 72 int cases; 73 scanf("%d",&cases); 74 while (cases--) 75 { 76 scanf("%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",&A.x,&A.y,&B.x,&B.y,&C.x,&C.y,&D.x,&D.y, 77 &ABspeed,&CDspeed,&otherspeed); //输入数据 78 POINT FIRST,SECOND,START,END; 79 START.x = A.x; START.y=A.y; END.x = B.x; END.y=B.y; 80 double T_FIRST=1,T_SECOND=0; 81 getThreePoint(START,END,&FIRST,&SECOND); 82 while(fabs(T_FIRST-T_SECOND)>EPS) 83 { 84 T_FIRST = getBestThreePoint(C,D,FIRST); 85 T_SECOND = getBestThreePoint(C,D,SECOND); 86 if(T_FIRST > T_SECOND) 87 { 88 START = FIRST; 89 getThreePoint(START,END,&FIRST,&SECOND); 90 } 91 else 92 { 93 END = SECOND; 94 getThreePoint(START,END,&FIRST,&SECOND); 95 } 96 } 97 printf("%.2f\n",T_FIRST); 98 } 99 return 0; 100 }