hud 3400——Line belt 题解(三分套三分)

hud 3400——Line belt 题解

问题描述

题目

给定两条线段AB ,CD 一个人在AB 上跑时速度为P,在CD上跑时速度为Q,在其他地方跑时速度为R。求从A点跑到D点的最短时间。

输入

  1. 测试数量T
  2. A点和B点坐标:Ax,Ay,Bx,By
  3. C点和D点的坐标:Cx,Cy,Dx,Dy
  4. P,Q ,R
  5. 以上输入均为整数

数据范围

0<=Ax,Ay,Bx,By,Cx,Cy,Dx,Dy<=1000,1<=P,Q,R<=10

输出

从A到D的最短时间,四舍五入两位小数

解题思路

双三分法(三分套三分)

题目大意和表示

  1. 大意: 从A点出发,先走到线段AB上的一点X,在走到线段CD上一点Y,最后走到D点。求出这一过程所需要的最短时间。
  2. 表示方式:
    ——>time=|AX|/P+|XY|/R+|YD|/Q
    这一点很好理解

分析

分析题目我们容易知道,要求这一段时间的最小值,一定可以在AB和CD上分别找到一点X和一点Y,使得走完整段路径的时间最短。

显然,X,Y都在移动,这样就使得AX,XY,YD这三条边的长度都在变化,这样显然不易于分析理解。

但是我们经过思考后可以知道,当X每位于一个坐标时,就一定有一条XY/R+YD/Q使得该情况的用时最短。XY/R+YD/Q的函数图像显然如下:
在这里插入图片描述
函数是一条抛物线,我们想要得出函数的最小值,就不能再使用二分法了,因为二分法要求数据一定时单调递增或递减的。在这里我们显然可以使用三分法来处理了。

XY/R+YD/Q最小值三分代码

double findCD(double ABx, double ABy) {
	int cx = Cx;
	int cy = Cy;
	int dx = Dx;
	int dy = Dy;
	double mid1x = cx + (dx - cx) / 3.0;
	double mid1y = cy + (dy - cy) / 3.0;
	double mid2x = cx + 2 * (dx - cx) / 3.0;
	double mid2y = cy + 2 * (dy - cy) / 3.0;
	double time1 = 0;
	double time2 = 0;
	do {
		time1 = sqrt((mid1x - dx) * (mid1x - dx) + (mid1y - dy) * (mid1y - dy)) / Q + 
		sqrt((ABx - mid1x) * (ABx - mid1x) + (ABy - mid1y) * (ABy - mid1y)) / R;
		time2= sqrt((mid2x - dx) * (mid2x - dx) + (mid2y - dy) * (mid2y - dy)) / Q + 
		sqrt((ABx - mid2x) * (ABx - mid2x) + (ABy - mid2y) * (ABy - mid2y)) / R;
		if (time1 < time2) {
			dx = mid2x;
			dy = mid2y;
		}
		else {
			cx = mid1x;
			cy = mid1y;
		}
	} while (abs(time1 - time2) > eps);
	return time1;
}

继续思考,每一个X都对应着一段XY~YD的最小时间值,这一X位置的时间总和为time=|AX|/P
+|XY|/R+|YD|/Q显然的每一个x位置的时间总和又构成了一个上图的抛物线函数,我们再使用依次三分法,将时间总和的最小值求出。

时间总和time=|AX|/P+|XY|/R+|YD|/Q最小值三分代码:

double findAB() {
	int ax = Ax;
	int ay = Ay;
	int bx = Bx;
	int by = By;
	double mid1x = ax + (bx - ax) / 3.0;
	double mid1y = ay + (by - ay) / 3.0;
	double mid2x = ax + 2 * (bx - ax) / 3.0;
	double mid2y = ay + 2*(by - ay) / 3.0;
	double time1 = 0;
	double time2 = 0;
	do {
		time1 = sqrt((ax - mid1x) * (ax - mid1x) + (ay - mid1y) * (ay - mid1y)) / P + 
		findCD(mid1x, mid1y);
		time2 = sqrt((ax - mid2x)*(ax - mid2x) + (ay - mid2y) * (ay - mid2y) / P + 
		findCD(mid2x, mid2y));
		if (time1 < time2) {
			bx = mid2x;
			by = mid2y;
		}
		else {
			ay = mid1y;
			ax = mid1x;
		}
	} while (abs(time1 - time2) > eps);
	
	return time1;
		
}

完整代码

#include<iostream>
using namespace std;
const double  eps = 1e-4;
int Ax, Ay, Bx, By;
int Cx, Cy, Dx, Dy;
int P, Q, R;
int T;
double findCD(double ABx, double ABy) {
	int cx = Cx;
	int cy = Cy;
	int dx = Dx;
	int dy = Dy;
	double mid1x = cx + (dx - cx) / 3.0;
	double mid1y = cy + (dy - cy) / 3.0;
	double mid2x = cx + 2 * (dx - cx) / 3.0;
	double mid2y = cy + 2 * (dy - cy) / 3.0;
	double time1 = 0;
	double time2 = 0;
	do {
		time1 = sqrt((mid1x - dx) * (mid1x - dx) + (mid1y - dy) * (mid1y - dy)) / Q + 
		sqrt((ABx - mid1x) * (ABx - mid1x) + (ABy - mid1y) * (ABy - mid1y)) / R;
		time2= sqrt((mid2x - dx) * (mid2x - dx) + (mid2y - dy) * (mid2y - dy)) / Q + 
		sqrt((ABx - mid2x) * (ABx - mid2x) + (ABy - mid2y) * (ABy - mid2y)) / R;
		if (time1 < time2) {
			dx = mid2x;
			dy = mid2y;
		}
		else {
			cx = mid1x;
			cy = mid1y;
		}
	} while (abs(time1 - time2) > eps);
	return time1;
}
double findAB() {
	int ax = Ax;
	int ay = Ay;
	int bx = Bx;
	int by = By;
	double mid1x = ax + (bx - ax) / 3.0;
	double mid1y = ay + (by - ay) / 3.0;
	double mid2x = ax + 2 * (bx - ax) / 3.0;
	double mid2y = ay + 2*(by - ay) / 3.0;
	double time1 = 0;
	double time2 = 0;
	do {
		time1 = sqrt((ax - mid1x) * (ax - mid1x) + (ay - mid1y) * (ay - mid1y)) / P + 
		findCD(mid1x, mid1y);
		time2 = sqrt((ax - mid2x)*(ax - mid2x) + (ay - mid2y) * (ay - mid2y) / P + 
		findCD(mid2x, mid2y));
		if (time1 < time2) {
			bx = mid2x;
			by = mid2y;
		}
		else {
			ay = mid1y;
			ax = mid1x;
		}
	} while (abs(time1 - time2) > eps);
	
	return time1;
		
}
int main() {
	cin >> T;
	while (T--) {
		cin >> Ax >> Ay >> Bx >> By;
		cin >> Cx >> Cy >> Dx >> Dy;
		cin >> P >> Q >> R;
		printf("%.2f", findAB());
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值