【二分路程】2020 acm-icpc (上海) D-Walker

1 篇文章 0 订阅

2020 acm-icpc (上海) D-Walker

题目

链接:https://ac.nowcoder.com/acm/contest/9925#question
在这里插入图片描述

示例

输入:
2
10000.0 1.0 0.001 9999.0 0.001
4306.063 4079.874 0.607 1033.423 0.847

输出
5001000.0000000000
3827.8370013755

题目大意

一条长为n的线段上有两个点,位置是p1,p2,他们各有一个恒定的速度v1,v2。你可以随时改变这两个点速度的方向(向左或向右)。问最快在什么时候能让线段上的每一个位置都被最少一个点走过。

思路

首先我们先默认p1<p2 [即,if(p1>p2) swap(p1,p2) ]
那么经过初步的思考,我们会发现可以分为以下3种情况:

1.由走得快的那个人单独走完全程
在这里插入图片描述
2.互相走到头
在这里插入图片描述

3.在p1,p2之间某点相遇(这里某些具体实现需特判相遇点为端点p1,p2的情况)
在这里插入图片描述
那么3种情况取最小值即结果。

具体思路

对于方案1和方案2,可以直接算出,不再赘述。
对于方案3,由于相遇点mid不确定且可能为p1-p2之间的任意位置,枚举所有情况不太现实,因此我们采用二分路程的方法来实现:
初始化令 l = p1,r = p2,mid (l+r)/2;
令t1是p1到相遇点mid的时间,t2是p2到相遇点mid的时间,则t1,t2很好算出。
最终结果ans = min(ans,max(t1,t2))
此时,若t1>t2,我们可以通过左移mid让t1变小,使得ans变小,即r = mid(向左二分)
否则,l = mid(向右二分)。
最后,对于二分次数的问题,由于n<=1000且精度要求1e-6,那么我们只需要做1000次的二分路程就一定能保证精度(r-l)在1e-6以内。(注意10000可能会T)

参考代码

(写得很乱.jpg)
(压行+合并相似的代码可以做到20行左右.jpg)

#include<bits/stdc++.h>
using namespace std;
double n,p1,v1,p2,v2;
double solve1(double p1,double v1,double mid){
	if(mid==p1)
	return p1/v1;     //特判相遇点在端点的情况(其实可以不用) 
	else{
	double d = mid - p1;
	double d1 = max(d,p1);
	double d2 = min(d,p1);
	return (2*d2+d1) / v1;   // p1到相遇点mid走过的距离 = 2*短的那段 + 1*长的那段 
	}
}

double solve2(double p2,double v2,double mid){
	if(mid==p2)
	return (n-p2)/v2;        //solve2与solve1同理
	else{
		double d = p2 - mid;
		double d1 = max(n - p2,d);
		double d2 = min(n - p2,d);
		return (2*d2+d1) / v2;
	}
}
double cal(double p,double v){
	double d1 = max(p,n-p);    
	double d2 = min(p,n-p);
	return (2*d2+d1)/v;   //一个人走全程需要走的路程
}
int main(){
	int t;
	cin>>t;
	while(t--){
		cin>>n>>p1>>v1>>p2>>v2;
		//默认p1<p2,便于后序的讨论 
		if(p1>p2){
			swap(p1,p2);
			swap(v1,v2);
		}
		// ans 即所求结果 
		double ans = 0x3f3f3f3f;
		//互相走到头的情况 
		ans = min( ans, max ( p2/v2, (n-p1) / v1 ) );
		// 一人负责全程的情况 
		ans = min( ans, min(cal(p1,v1) , cal(p2,v2)));
		// 在p1-p2之间相遇的情况(二分路程) 
		double l = p1,r = p2;
		double mid = 0;
		int cnt = 0;
		while(cnt<=1000){
			mid = (l+r)/2.0;
			double t1 = solve1(p1,v1,mid);  //p1到当前的相遇点mid花费的时间 
			double t2 = solve2(p2,v2,mid);	//p2到当前的相遇点mid花费的时间 
			ans = min(ans,max(t1,t2));
			if(t1>t2)
			r = mid;  //二分,相遇点左移 
			else
			l = mid;  //二分,相遇点右移 
			cnt++;
		}
		printf("%.10f\n",ans);
	}
	return 0;
} 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值