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),再取ABmmid点,同样对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 }

 

 

 

posted @ 2013-05-06 12:53  seayar  阅读(190)  评论(0编辑  收藏  举报