XIV Open Cup named after E.V. Pankratiev. GP of America B.Bob and Banjo

XIV Open Cup named after E.V. Pankratiev. GP of America B.Bob and Banjo

题目大意

给你两个点和一个圆,要求穿过圆的任何一段路径都不超过 $t$ ,问最短路径长度是多少。保证两个点都不在圆内。

题目分析

首先,如果两个点的连线不经过圆,那么答案就是他们二者的距离。

考虑如果两个点的连线经过圆。那么显然应该是先走到圆,之后顺着圆的边上走,之后再离开圆走到终点。在如果固定在圆上的起始点和终止点,那么可以通过先不停的走长度为 $t$ 的割线来构造最短的路径。圆上的起始点和终止点的移动范围显然是每个点与圆的两条切线之间。而显然移动这两个点的时候会出现极小值。通过使用三分套三分即可解决。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>

using namespace std;

int n, m;

double eps = 1e-8;
double pi = 3.1415926535897932384626433;

double xa, xb, ya, yb, xc, yc, R, t;

int read(){
	int x;
	scanf("%d", &x);
	return x;
}

double sqr(double x){
	return x * x;
}

double dist(double x, double y, double a, double b){
	return sqrt(sqr(x - a) + sqr(y - b));
}

bool cross(){
	for(int i=0;i<=1e5;i++){
		double x = (xb - xa) * (double)i / 1e5 + xa;
		double y = (yb - ya) * (double)i / 1e5 + ya;
		if(dist(x, y, xc, yc) - R <= eps){
			return true;
		}
	}
	
	return false;
}

double checkb(double a, double b){
	double x = b - a;
	double y = a - b;
	double d = asin((t / 2.0) / R) * 2.0;
	double ans = 0;
	
	if(t - 2.0 * R >= eps){
		return dist(xb, yb, xc + cos(b) * R, yc + sin(b) * R) + dist(xc + cos(a) * R, yc + sin(a) * R, xc + cos(b) * R, yc + sin(b) * R);
	}
	
	while(x - 2 * pi > eps)x -= 2 * pi;
	while(x < eps)x += 2 * pi;
	while(y - 2 * pi > eps)y -= 2 * pi;
	while(y < eps)y += 2 * pi;
	
	x = min(x, y);
	
	if(t <= eps or d <= eps){
		ans = R * x;
	}else{
		y = floor(x / d);
		ans += y * t;
		y = x - y * d;
		ans += 2.0 * sin(y / 2.0) * R;
	}
	return dist(xb, yb, xc + cos(b) * R, yc + sin(b) * R) + ans;
}

double check(double a){
	int i, j;
	double l, r;
	double midl, midr;
	double d = acos((xb - xc) / dist(xb, yb, xc, yc));
	
	if(yc - yb >= eps){
		d = 2 * pi - d;
	}
	
	l = d - acos(R / dist(xb, yb, xc, yc));
	r = d + acos(R / dist(xb, yb, xc, yc));
	
	for(i=1;i<=100;i++){
		midl = (2.0 * l + r) / 3.0;
		midr = (l + 2.0 * r) / 3.0;
		
		if(checkb(a, midl) < checkb(a, midr)){
			r = midr;
		}else{
			l = midl;
		}
	}
	
	return dist(xa, ya, xc + cos(a) * R, yc + sin(a) * R) + checkb(a, l);
}

int solve(){
	int i, j;
	double l, r;
	double midl, midr;
	double d = acos((xa - xc) / dist(xa, ya, xc, yc));
	
	if(yc - ya >= eps){
		d = 2 * pi - d;
	}
	
	l = d - acos(R / dist(xa, ya, xc, yc));
	r = d + acos(R / dist(xa, ya, xc, yc));
	
	for(i=1;i<=100;i++){
		midl = (2.0 * l + r) / 3.0;
		midr = (l + 2.0 * r) / 3.0;
		
		if(check(midl) < check(midr)){
			r = midr;
		}else{
			l = midl;
		}
	}
	
	printf("%.6f\n", check(l));
	
	return 0;
}

int main(){
	int i, j;
	
	while(~scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &xa, &ya, &xb, &yb, &xc, &yc, &R, &t)){
		if(xa == 0 and ya == 0 and xb == 0 and yb == 0 and xc == 0 and yc == 0 and R == 0 and t == 0){
			break;
		}
		
		if(!cross()){
			printf("%.6f\n", dist(xa, ya, xb, yb));
		}else{
			solve();
		}
	}
	
	return 0;
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据