hdu3400 三分法

 

题目 http://acm.hdu.edu.cn/showproblem.php?pid=3400

题目大意 :求从线段AB顶点A到线段CD顶点D的最少时间,其中在AB上的速度为P

CD上的速度为D,在其它地方的速度为R

算法 :三分法  hdu3400 <wbr>三分法hdu3400 <wbr>三分法

思路 :设时间为tAB上某点K1CD上某点K2,则路径是A->K1->K2->D(没必要在空平面内走折线)假定我们知道最短时间下的AB上的点是K1,这问题便成为了在CD上找一点,使得总最小。我们设这点为(xy)由于速度都是常数,则有:(式中除xy都是常数)

Time = |AK1|/P + sqrt( (x – k1.x)^2 + (y – K2.x)^2 ) + sqrt( (x – D.x)^2 + (y – D.y)^2) 可简化为

Z = (x – a)^2 + (y - b) ^2 + C, 是抛物面,其中一条弧是单峰函数。同理,K1的确定因为类似。可用三分法。 (证明不很严谨,只为安慰自己)

值得注意的一点是精度如何控制。一开始用的是whilelen(left ,right) > ESP,用的是三分区间的左右断点进行的判断,wa了无数次,最后听杨大牛说,“求什么,就应该用什么作为进度的约束条件,而不应该是左右区间,有可能左右区间的进度的在1e-6,而通过这个算出来的最小时间在1e-2级别,这样解就错了。应此切记,求什么就用什么作为精度控制。

 

AC code

 

#include <stdio.h>

#include <math.h>

#include <stdlib.h>

#define ESP 1E-5

struct POINT{

    double x, y;

} A, B, C, D;

int P, Q, R;

 

POINT Get_Mid(POINT PL, POINT PR){

    POINT temp;

    temp.x = (PL.x + PR.x) / 2.0;

    temp.y = (PL.y + PR.y) / 2.0;

    return temp;

}

 

doublelen(POINT p1, POINT p2){

    return sqrt(ESP + (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));

}

 

double Get_Min_Time(POINT pt){

    POINT left = C, right = D, mid1, mid2;

    double mid1_area = 0, mid2_area = 1;

    while(fabs(mid2_area - mid1_area) > ESP){

       mid1 = Get_Mid(left, right);

       mid2 = Get_Mid(mid1, right);

       mid1_area = len(pt, mid1) / R + len(mid1, D) / Q;

        mid2_area = len(pt, mid2) / R + len(mid2, D) / Q;

       if(mid1_area <= mid2_area) right = mid2;

       else left = mid1;

    }

    return mid1_area + len(A, pt) / P ;

}

 

int main(){

    int CASE;

    POINT left, mid1, mid2, right;

    scanf("%d", &CASE);

    while(CASE --){

       scanf("%lf %lf %lf %lf %lf %lf %lf %lf %d %d %d", &A.x, &A.y, &B.x, &B.y, &C.x, &C.y, &D.x, &D.y, &P, &Q, &R);

       left = A;

       right = B;

       double mid1_area = 0, mid2_area = 1;

       while(fabs(mid1_area - mid2_area) > ESP){

           mid1 = Get_Mid(left, right);

           mid2 = Get_Mid(mid1, right);

           mid1_area = Get_Min_Time(mid1);

           mid2_area = Get_Min_Time(mid2);

           if(mid1_area <= mid2_area) right = mid2;

           else left = mid1;

       }

       printf("%.2lf\n", mid1_area);

    }

    return 0;

}

 

 

   

      

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值