跑步(简易版) 题解

#5285. YAOI Round #26 (Div.1+Div.2) C. 跑步(简易版)


解法 1

跑道上在两个人之间选一个位置,使得自己有最长的时间不与他们相遇。

根据题目条件,很容易发现自己与 A 相距 \(y\),与 B 相距 \(x-y\)

因此自己与 A 相遇的时间即为 \(\dfrac{y}{v_1-v_0}\),与 B 相遇的时间为 \(\dfrac{x-y}{v_0-v_2}\)。题目求与两人中任意一人相遇的时间最长,可以转换为使这两个等式相等(即同时相遇,时间为最长的)(基础的小学数学)。因此简单地推推柿子:

\[\begin{aligned}\\ \dfrac{y}{v_1-v_0} &= \dfrac{x-y}{v_0-v_2}\\ y \times (v_0-v_2) &= (x-y) \times (v_1-v_0)\\ y \times (v_0-v_2+v_1-v_0) &= x \times (v_1-v_0)\\ y \times (v_1-v_2) &= x \times (v_1-v_0)\\ y &= \dfrac{x \times (v_1-v_0)}{(v_1-v_2)}\\ \end{aligned}\]

得到 \(y = \dfrac{x \times (v_1-v_0)}{(v_1-v_2)}\),而等式右边所有的量都是已知的,因此可以直接代入计算。


解法 2

如果上面推柿子看不懂的可以来看看这种解法。

首先我们拿样例出来手推一下答案:

y 4 8 12 16 20 24 28
ans(追及时间) 1 2 3 4 5 3 1

把得到的结果画一个图出来,可以发现是一个单峰函数(中间凸起,最高点左侧单调上升,右侧单调下降)。因此我们可以直接三分或者使用爬山算法(两种很常用的算法,尤其是后者和模拟退火,建议自行学习)。


代码

公式

#include<bits/stdc++.h>
using namespace std;

int main(){
	double x,v0,v1,v2;
    scanf("%lf%lf%lf%lf",&x,&v0,&v1,&v2);
	printf("%.4lf",x*(v1-v0)/(v1-v2));
	return 0;
}

三分

#include<bits/stdc++.h>
using namespace std;

int x,v0,v1,v2;
double l,r,mid,eps=1e-6;

double getans(double y){
	return min(y/(v1-v0),(x-y)/(v0-v2));
}

int main(){
	scanf("%d%d%d%d",&x,&v0,&v1,&v2);
	l=0,r=x;
	while(r-l>=eps){
		mid=(l+r)/2;
		if(getans(mid-eps)<getans(mid+eps))l=mid;
		else r=mid;
	}
	printf("%.4lf",mid);
	return 0;
}
posted @ 2022-10-06 12:29  ofbwyx  阅读(80)  评论(0)    收藏  举报