【二分查找】P9822 [ICPC2020 Shanghai R] Walker【有误差】|普及

本文涉及的基础知识点

C++二分查找

[ICPC2020 Shanghai R] Walker

题面翻译

作为著名的旅行者,Prof. Pang 的研究兴趣是在一生中到尽可能多的地方旅游。

在一条 [ 0 , n ] [0,n] [0,n] 的线段上有两名旅行者,第一名在位置 p 1 p_1 p1 以 v 1 v_1 v1 (每秒钟可以走 v 1 v_1 v1 单位的距离)的速度开始运动,第二名在位置 p 2 p_2 p2 以 v 2 v_2 v2 的速度开始运动。

从他们分别的起始点出发,旅行者可以在线段上移动,但不可以走出线段。他们可以随时改变自己的方向。

请帮助 Prof. Pang 计算至少需要多少时间,线段上的每个位置都至少被一名旅行者经过。

题目描述

As a world-famous traveler, Prof. Pang's research interest is to travel as many places as possible in his life.

We have a segment [ 0 , n ] [0, n] [0,n]. There are two travelers on it. The first one is on position p 1 p_1 p1 with velocity v 1 v_1 v1 (which means s/he can walk v 1 v_1 v1 unit on the segment per second). The second one is on position p 2 p_2 p2 with velocity v 2 v_2 v2.

From their respective beginning points, travelers can walk on the segment. They cannot walk outside the segment. Whenever they want to change their direction, they can turn around immediately.

Please help Prof. Pang to calculate the minimum possible time by which every position of the segment is passed by at least one traveler.

输入格式

The first line contains one integer t e s t ( 1 ≤ t e s t ≤ 10000 ) test~(1\le test\le 10000) test (1≤test≤10000) -- the number of test cases.

The i i i-th of the next t e s t test test lines contains five numbers n , p 1 , i , v 1 , i , p 2 , i , v 2 , i n, p_{1, i}, v_{1, i}, p_{2, i}, v_{2, i} n,p1,i,v1,i,p2,i,v2,i ( 0 < n ≤ 10000 0 < n \le 10000 0<n≤10000, 0 ≤ p 1 , i , p 2 , i ≤ n 0\le p_{1, i},p_{2, i} \le n 0≤p1,i,p2,i≤n, 0.001 ≤ v 1 , i , v 2 , i ≤ 1000 0.001 \le v_{1, i},v_{2, i} \le 1000 0.001≤v1,i,v2,i≤1000). All numbers have at most 3 3 3 digits after the decimal point.

输出格式

For each test case, we should output one number -- the minimum time that every position of the segment is passed by at least one traveler.

Your answer is considered correct if its absolute or relative error does not exceed 10 − 6 10^{-6} 10−6.

样例 #1

样例输入 #1

复制代码
2
10000.0 1.0 0.001 9999.0 0.001
4306.063 4079.874 0.607 1033.423 0.847

样例输出 #1

复制代码
5001000.0000000000
3827.8370013755

二分查找

性质一 :任意旅行者,走到最右的距离是r,最左的距离是left,则经过的范围是[left,r]。为使得r最大化,left最小化。不劣解:两种可能先左后右、先右后左。某解先到达left®,则不劣解: p → l e f t ( r ) → r ( l e f t ) p \rightarrow left(r) \rightarrow r(left) p→left(r)→r(left)。
性质二 :一个旅行者经过两个端点(0和n)    ⟺    \iff ⟺ 经过全程。
情况一 :一个人走完全部旅途即一个人经过两个端点(0和n)。
情况二 :第一人经过0,第二人经过n。
情况三 :第一人经过n,第二人到达0。
二分

Cnt0(mid,p,v)如果无法到达0,则返回-1。如果能到达0,确保到达0,能到达的最大坐标。前提条件vmid >= p
先左后右,max(v
mid-p,p) 先右后左:(vmid-p)/2+p
Cntn(mid,p,v)确保到达n的情况下,经过的最小坐标。前提条件 v
mid >= n-p,否则返回n+1

先左后右:p-(vmid-(n-p))/2 先右后左 min(p,n-(v mid-(n-p)))
注意 :除异常外,Cnt0和Cntn返回值[0,n]。

二分类型:寻找首端。

参数范围:[0,1e9]

二分继续条件:r - left >= 1e-7。

Check函数:Cnt0 >= Cnt1

代码

误差要求1e-6,实际2.9e-6

cpp 复制代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>

#include <bitset>
using namespace std;



template<class T = int>
vector<T> Read(int n,const char* pFormat = "%d") {
	vector<T> ret(n);
	for(int i=0;i<n;i++) {
		scanf(pFormat, &ret[i]);	
	}
	return ret;
}

template<class T = int>
vector<T> Read( const char* pFormat = "%d") {
	int n;
	scanf("%d", &n);
	vector<T> ret;
	T d;
	while (n--) {
		scanf(pFormat, &d);
		ret.emplace_back(d);
	}
	return ret;
}

string ReadChar(int n) {
	string str;
	char ch;
	while (n--) {
		do
		{
			scanf("%c", &ch);
		} while (('\n' == ch));
			str += ch;
	}
	return str;
}
template<class T1,class T2>
void ReadTo(pair<T1, T2>& pr) {
	cin >> pr.first >> pr.second;
}

template<class INDEX_TYPE>
class CBinarySearch
{
public:
	CBinarySearch(INDEX_TYPE iMinIndex, INDEX_TYPE iMaxIndex, INDEX_TYPE tol = 1) :m_iMin(iMinIndex), m_iMax(iMaxIndex), m_iTol(tol) {}
	template<class _Pr>
	INDEX_TYPE FindFrist(_Pr pr)
	{
		auto left = m_iMin - m_iTol;
		auto rightInclue = m_iMax;
		while (rightInclue - left > m_iTol)
		{
			const auto mid = left + (rightInclue - left) / 2;
			if (pr(mid))
			{
				rightInclue = mid;
			}
			else
			{
				left = mid;
			}
		}
		return rightInclue;
	}
	template<class _Pr>
	INDEX_TYPE FindEnd(_Pr pr)
	{
		INDEX_TYPE leftInclude = m_iMin;
		INDEX_TYPE right = m_iMax + m_iTol;
		while (right - leftInclude > m_iTol)
		{
			const auto mid = leftInclude + (right - leftInclude) / 2;
			if (pr(mid))
			{
				leftInclude = mid;
			}
			else
			{
				right = mid;
			}
		}
		return leftInclude;
	}
protected:
	const INDEX_TYPE m_iMin, m_iMax, m_iTol;
};
class Solution {
		public:
			double Ans(double n,double p1,double v1,double p2,double v2) {
				auto Cnt0 = [&](double mid, double p, double v) {
					const double remain = v * mid - p;
					if (remain < 0) { return - 1.0; }
					auto ans= max(remain, p+remain/2);
					return min(n,ans);
				};
				auto Cntn = [&](double mid, double p, double v) {
					const double remain = v * mid - (n - p);
					if (remain < 0 ) { return n+1; }
					auto ans = min(p - remain/2 , n- remain);
					return max(0.0,ans);
				};
				auto Check = [&](double mid) {
					if((Cnt0(mid,p1,v1)>=n)|| (Cnt0(mid, p2, v2) >= n)|| 
						(Cntn(mid, p1, v1) <= 0) || (Cntn(mid, p2, v2) <= 0)) {
						return true;
					}
					if (Cnt0(mid, p1, v1) >= Cntn(mid, p2, v2)) {
						return true; }
					return Cnt0(mid, p2, v2) >= Cntn(mid, p1, v1);
				};	
				return CBinarySearch<double>(0, 1e9, 1e-7).FindFrist(Check);
				}
			const double m_tol = 1e-10;
		};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG
	int T;	
	cin >> T;
	while (T--) {
		double n, p1, v1, p2, v2;
		cin >> n >> p1 >> v1 >> p2 >> v2;
		auto res = Solution().Ans(n,p1,v1,p2,v2);
		cout << res << std::endl;
	}
	
#ifdef _DEBUG			
		/*Out(mat, "mat=");
		Out(L, "L=");
		printf(",Q=%d;", Q);*/
#endif			
	return 0;
}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关推荐
leiming61 分钟前
c++ find 算法
算法
CoovallyAIHub3 分钟前
YOLOv12之后,AI在火场如何进化?2025最后一篇YOLO论文揭示:要在浓烟中看见关键,仅靠注意力还不够
深度学习·算法·计算机视觉
梭七y4 分钟前
【力扣hot100题】(121)反转链表
算法·leetcode·链表
mg6685 分钟前
0基础开发学习python工具_____用 Python + Pygame 打造绚丽烟花秀 轻松上手体验
开发语言·python·学习·pygame
qq_433554546 分钟前
C++字符串hash
c++·算法·哈希算法
无限进步_6 分钟前
【C语言】堆(Heap)的数据结构与实现:从构建到应用
c语言·数据结构·c++·后端·其他·算法·visual studio
再难也得平7 分钟前
两数之和和字母异位词分组
数据结构·算法
CodeOfCC17 分钟前
C++ 实现ffmpeg解析hls fmp4 EXT-X-DISCONTINUITY并支持定位
开发语言·c++·ffmpeg·音视频
ghie909017 分钟前
基于LSB匹配的隐写术MATLAB实现
开发语言·计算机视觉·matlab
Lhan.zzZ19 分钟前
Qt绘制残留问题排查与修复日志
开发语言·数据库·qt