三分法

目录

一,三分法

二,代码示例

三,OJ实战

力扣 852. 山脉数组的峰顶索引(三分)

HDU 2899 Strange fuction(二分or三分)

HDU 2298 Toxophily(三分+二分)

HDU 3400 Line belt(三分套三分)

HDU 4355 Party All the Time(二分or三分)

SGU 114 广播电台(二分or三分)

 POJ 2366 Sacrament of the sum(两个数的和,二分or not二分)

POJ 2785 Values whose Sum is 0(四个数的和,二分or not二分)

HDU 4071 Trick or Treat(三分,why?二分,wow!)


一,三分法

求一个单峰函数的峰值时,如果可以求导,那么可以用二分法求极值,如果不能求导(包括散点函数,即x和y都只能是整数),那么就只能用三分法来求。

凸函数和凹函数都属于单峰函数,但单峰函数不一定是凸函数或者凹函数

凸函数三分法:

已知函数f在[a,b]内是凸的,求区间[a,b]内的极大值。

任取c,d使得a<c<d<b,如果f(c)<f(d)则f的极大值在[c,b]内,否则就在[a,d]内。

凹函数同理。

先递增后递减的单峰函数,都和凸函数同理,先递减后递增的单峰函数,都和凹函数同理

凸函数之和是凸函数,凹函数之和是凹函数,单峰函数之和未必是单峰函数。

对于非严格递增/减的函数,可以使用二分法,但是不能使用三分法

二,代码示例

 先递减后递增的函数,三分法:

while (low + 0.000001 < high)
{
		mid1 = (low + high) / 2;
		mid2 = (mid1 + high) / 2;
		if (f(mid1) < f(mid2))high = mid2;
		else low = mid1;
}

三,OJ实战

力扣 852. 山脉数组的峰顶索引(三分)

符合下列属性的数组 arr 称为 山脉数组 :
arr.length >= 3
存在 i(0 < i < arr.length - 1)使得:
arr[0] < arr[1] < ... arr[i-1] < arr[i]
arr[i] > arr[i+1] > ... > arr[arr.length - 1]
给你由整数组成的山脉数组 arr ,返回任何满足 arr[0] < arr[1] < ... arr[i - 1] < arr[i] > arr[i + 1] > ... > arr[arr.length - 1] 的下标 i 。

示例 1:

输入:arr = [0,1,0]
输出:1
示例 2:

输入:arr = [0,2,1,0]
输出:1
示例 3:

输入:arr = [0,10,5,2]
输出:1
示例 4:

输入:arr = [3,4,5,1]
输出:2
示例 5:

输入:arr = [24,69,100,99,79,78,67,36,26,19]
输出:2
 

提示:

3 <= arr.length <= 104
0 <= arr[i] <= 106
题目数据保证 arr 是一个山脉数组
 

进阶:很容易想到时间复杂度 O(n) 的解决方案,你可以设计一个 O(log(n)) 的解决方案吗?

class Solution {
public:
	int peakIndexInMountainArray(vector<int>& arr) {
		int low = 0, high = arr.size() - 1;
		while (low < high - 2) {
			int mid = (low + high) / 2;
			int m2 = (mid + high) / 2;
			if (arr[mid] < arr[m2])low = mid;
			else high = m2;
		}
		while (low < arr.size() - 1 && arr[low] < arr[low + 1])low++;
		return low;
	}
};

HDU 2899 Strange fuction(二分or三分)

题目:

Description

Now, here is a fuction: 
  F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100) 
Can you find the minimum value when x is between 0 and 100.

Input

The first line of the input contains an integer T(1<=T<=100) which means the number of test cases. Then T lines follow, each line has only one real numbers Y.(0 < Y <1e10)

Output

Just the minimum value (accurate up to 4 decimal places),when x is between 0 and 100.

Sample Input

2 100 200

Sample Output

-74.4291 -178.8534

这个题目和DU 2199 Can you solve this equation?差不多,不过没有了昨天那个EPS的精度的问题。

很容易看出来,F(x)在区间(0,100)就是先递减后递增,利用这个作为二分查找时选择左右的依据即可。

二分代码:

#include<stdio.h>

int main()
{
	int t;
	scanf("%d", &t);
	double y;
	while (t--)
	{
		scanf("%lf", &y);
		double low = 0, high = 100;
		double m;
		double mmm;
		while (low + 0.0000001 < high)
		{
			m = (low + high) / 2;
			mmm = m*m*m;
			if (((42 * mmm + 48 * m*m)*mmm + 21 * m*m + 10 * m)>y)high = m;
			else low = m;
		}
		printf("%.4lf\n", (6*m+8)*mmm*mmm+7*mmm+5*m*m-y*m);
	}
	return 0;
}

用三分法来做,不涉及到导数。

因为函数本身就是单峰的,所以很简单,没什么技巧。

三分代码:

#include<stdio.h>

int main()
{
	int t;
	scanf("%d", &t);
	double y;
	while (t--)
	{
		scanf("%lf", &y);
		double low = 0, high = 100;
		double mid,mm;
		while (low + 0.0000001 < high)
		{
			mid = (low + high) / 2;
			mm = (mid + high) / 2;
			if (((((mid * 6 + 8)*mid*mid*mid + 7)*mid + 5)*mid*mid - y*mid)<((((mm * 6 + 8)*mm*mm*mm + 7)*mm + 5)*mm*mm - y*mm))high = mm;
			else low = mid;
		}
		printf("%.4lf\n", ((((mid * 6 + 8)*mid*mid*mid + 7)*mid + 5)*mid*mid - y*mid));
	}
	return 0;
}

HDU 2298 Toxophily(三分+二分)

题目:

Description

The recreation center of WHU ACM Team has indoor billiards, Ping Pang, chess and bridge, toxophily, deluxe ballrooms KTV rooms, fishing, climbing, and so on. 
We all like toxophily. 

Bob is hooked on toxophily recently. Assume that Bob is at point (0,0) and he wants to shoot the fruits on a nearby tree. He can adjust the angle to fix the trajectory. Unfortunately, he always fails at that. Can you help him? 

Now given the object's coordinates, please calculate the angle between the arrow and x-axis at Bob's point. Assume that g=9.8N/m. 

Input

The input consists of several test cases. The first line of input consists of an integer T, indicating the number of test cases. Each test case is on a separated line, and it consists three floating point numbers: x, y, v. x and y indicate the coordinate of the fruit. v is the arrow's exit speed. 
Technical Specification 

1. T ≤ 100. 
2. 0 ≤ x, y, v ≤ 10000. 

Output

For each test case, output the smallest answer rounded to six fractional digits on a separated line. 
Output "-1", if there's no possible answer. 
Please use radian as unit. 

Sample Input

3

0.222018 23.901887 121.909183

39.096669 110.210922 20.270030

138.355025 2028.716904 25.079551

Sample Output

1.561582

-1

-1

我定义了一个中间变量double s = 9.8*x*x / v / v + y;

假设答案的角度为a,a应该满足x*sin(2*a)-y*cos(2*a)-s=0

但是x*sin(2*a)-y*cos(2*a)-s这个函数是单峰的,不是完全单调的。

它有一个极大值,还有2个零点,或者2个零点在极大值的地方重合,或者没有零点。

可以先用辅助角公式判断有没有零点,然后用三分法求出任何一个函数值不小于0的点,

然后求出这个点左边的零点(或者就是这个点本身)

发现只有我是15ms,前面过的人都是0ms,估计他们不是这个方法,可能是直接用三分法就出来了,我没有再仔细考虑了,感觉方法差不多,而且复杂度也是一样的。

#include<stdio.h>
#include<math.h>

int main()
{
	int t;
	scanf("%d", &t);
	double x, y, v;
	while (t--)
	{
		scanf("%lf%lf%lf", &x, &y, &v);
		double r = sqrt(x*x + y*y);
		double s = 9.8*x*x / v / v + y;
		if (s>r)printf("%.d\n", -1);
		else
		{
			double low = 0, high = acos(-1.0);
			double mid, mm;
			while (low < high)
			{
				mid = (low + high) / 2;
				mm = (mid + high) / 2;
				if (x*sin(mid)-y*cos(mid)<x*sin(mm)-y*cos(mm))low = mid;
				else high = mm;
				if ((x*sin(mid) - y*cos(mid))>=s)break;
			}
			low = 0;
			high = mid;
			while (low + 0.000000001 < high)
			{
				mid = (low + high) / 2;
				if (x*sin(mid) - y*cos(mid)<s)low = mid;
				else high = mid;
			}
			printf("%.6lf\n", mid/2);
		}
	}
	return 0;
}

HDU 3400 Line belt(三分套三分)

题目:

Description

In a two-dimensional plane there are two line belts, there are two segments AB and CD, lxhgww's speed on AB is P and on CD is Q, he can move with the speed R on other area on the plane. 
How long must he take to travel from A to D?

Input

The first line is the case number T. 
For each case, there are three lines. 
The first line, four integers, the coordinates of A and B: Ax Ay Bx By. 
The second line , four integers, the coordinates of C and D:Cx Cy Dx Dy. 
The third line, three integers, P Q R. 
0<= Ax,Ay,Bx,By,Cx,Cy,Dx,Dy<=1000 
1<=P,Q,R<=10

Output

The minimum time to travel from A to D, round to two decimals.

Sample Input

1 0 0 0 100 100 0 100 100 2 2 1

Sample Output

136.60

就是求从A到E到F到D的最短时间。
这个题目,就是所谓的三分套三分。
假设AE/AB=mid,DF/CD=t
对每个点E,可以求出E到F到D的最短时间,这个我用函数f 实现了。
因为E到D的时间 f(t) 是凹函数,所以很容易用三分求出来。
但是总时间是不是关于mid即E点的位置是单峰函数,这个还有待考虑。
感觉应该是的,不过又证明不了。
有一个定理倒是和这个类似,n个上凸函数的和一定是上凸函数。

代码:

#include<iostream>
#include<math.h>
#include<iomanip>
using namespace std;
 
double time(double x, double y, int cx, int cy, int dx, int dy, double t,int r,int q)
{
	double l = sqrt((dx - cx)*(dx - cx) + (dy - cy)*(dy - cy));
	double x2 = t*cx + (1 - t)*dx;
	double y2 = t*cy + (1 - t)*dy;
	return sqrt((x - x2)*(x - x2) + (y - y2)*(y - y2))/r+l*t/q;
}
 
double f(double x, double y, int cx, int cy, int dx, int dy,int r,int q)
{
	double low = 0, high = 1;
	double mid, mm;	
	while (low + 0.0000001 < high)
	{
		mid = (low + high) / 2;
		mm = (mid + high) / 2;
		if (time(x,y,cx,cy,dx,dy,mid,r,q)<time(x,y,cx,cy,dx,dy,mm,r,q))high = mm;
		else low = mid;
	}
	return time(x, y, cx, cy, dx, dy, mid, r, q);
}
 
int main()
{
	int t;
	int ax, ay, bx, by, cx, cy, dx, dy;
	int p, q, r;
	cin >> t;
	while (t--)
	{
		cin >> ax >> ay >> bx >> by >> cx >> cy >> dx >> dy;
		cin >> p >> q >> r;
		double low = 0, high = 1;
		double mid, mm;
		double l = sqrt((ax - bx)*(ax - bx) + (ay - by)*(ay - by));
		while (low + 0.00000001 < high)
		{
			mid = (low + high) / 2;
			mm = (mid + high) / 2;
if (f(mid*bx + (1 - mid)*ax, mid*by + (1 - mid)*ay, cx, cy, dx, dy, r, q) + l*mid / p < 
f(mm*bx + (1 - mm)*ax, mm*by + (1 - mm)*ay, cx, cy, dx, dy, r, q) + l*mm / p)high = mm;
			else low = mid;
		}
		cout <<fixed<<setprecision(2)<< 
f(mid*bx + (1 - mid)*ax, mid*by + (1 - mid)*ay, cx, cy, dx, dy, r, q) + l*mid / p << endl;
	}
	return 0;
}

HDU 4355 Party All the Time(二分or三分)

题目:

Description

In the Dark forest, there is a Fairy kingdom where all the spirits will go together and Celebrate the harvest every year. But there is one thing you may not know that they hate walking so much that they would prefer to stay at home if they need to walk a long way.According to our observation,a spirit weighing W will increase its unhappyness for S 3*W units if it walks a distance of S kilometers. 
Now give you every spirit's weight and location,find the best place to celebrate the harvest which make the sum of unhappyness of every spirit the least.

Input

The first line of the input is the number T(T<=20), which is the number of cases followed. The first line of each case consists of one integer N(1<=N<=50000), indicating the number of spirits. Then comes N lines in the order that x [i]<=x [i+1] for all i(1<=i<N). The i-th line contains two real number : X i,W i, representing the location and the weight of the i-th spirit. ( |x i|<=10 6, 0<w i<15 )

Output

For each test case, please output a line which is "Case #X: Y", X means the number of the test case and Y means the minimum sum of unhappyness which is rounded to the nearest integer.

Sample Input

1

4

0.6 5

3.9 10

5.1 7

8.4 10

Sample Output

Case #1: 832

题意:

有n个精灵在一条直线上,求一个点作为它们的集中地点,使得总的代价最小。假设一个精灵要走的距离是S,那么它的代价就是S^3*W,W是它的权重

注意到,总代价是∑(|x-xi|^3*wi),函数f(x)=|x-xi|^3*wi是凹函数,凹函数之和也是凹函数。

所以可以用三分法来做。

错误的三分代码:

#include<stdio.h>

double list1[50000];
double list2[50000];

int main()
{
	int t, n;
	scanf("%d", &t);
	double low, high, mid;
	double sum;
	int temp;
	for (int ii = 0; ii < t;ii++)
	{
		scanf("%d", &n);
		low = 1000000;
		high = -1000000;
		for (int i = 0; i < n; i++)
		{
			scanf("%lf%lf", &list1[i], &list2[i]);
			if (low > list1[i])low = list1[i];
			if (high<list1[i])high = list1[i];
		}
		while (low + 0.0000001 < high)
		{
			mid = (low + high) / 2;
			sum = 0;
			for (int i = 0; i < n; i++)
			{
				if (list1[i] < mid)sum += (list1[i] - mid)*(list1[i] - mid)*list2[i];
				else sum -= (list1[i] - mid)*(list1[i] - mid)*list2[i];
			}
			if (sum>0)high = mid;
			else low = mid;
		}
		sum = 0;
		for (int i = 0; i < n; i++)
		{
			temp = 1;
			if (list1[i] < mid)temp = -1;
			sum += temp*(list1[i] - mid)*(list1[i] - mid)*(list1[i] - mid)*list2[i];
		}
		printf("Case #%d: %.0f\n", ii + 1, sum);
	}
	return 0;
}

不知道为什么是wrong answer

实在找不出来,我就准备先不管它,先把二分的算法也写出来。

因为∑(|x-xi|^3*wi)是凹函数,而且导数很好求,所以这就是一个普通的求函数极小值的问题。

正确的二分代码:

#include<stdio.h>

double list1[50000];
double list2[50000];

int main()
{
	int t, n;
	scanf("%d", &t);
	double low, high, mid;
	double sum;
	int temp;
	for (int ii = 0; ii < t;ii++)
	{
		scanf("%d", &n);
		low = 1000000;
		high = -1000000;
		for (int i = 0; i < n; i++)
		{
			scanf("%lf%lf", &list1[i], &list2[i]);
			if (low > list1[i])low = list1[i];
			if (high<list1[i])high = list1[i];
		}
		mid = (low + high) / 2;		//关键
		while (low + 0.0000001 < high)
		{
			mid = (low + high) / 2;
			sum = 0;
			for (int i = 0; i < n; i++)
			{
				if (list1[i] < mid)sum += (list1[i] - mid)*(list1[i] - mid)*list2[i];
				else sum -= (list1[i] - mid)*(list1[i] - mid)*list2[i];
			}
			if (sum>0)high = mid;
			else low = mid;
		}
		sum = 0;
		for (int i = 0; i < n; i++)
		{
			temp = 1;
			if (list1[i] < mid)temp = -1;
			sum += temp*(list1[i] - mid)*(list1[i] - mid)*(list1[i] - mid)*list2[i];
		}
		printf("Case #%d: %.0f\n", ii + 1, sum);
	}
	return 0;
}

其中有一行是关键,在while循环之前要对mid进行初始化,否则对于low=high的情况是无法求解的。

发现这个问题的时候才知道为什么前面的三分法代码错了。

SGU 114 广播电台(二分or三分)

描述

Berland的每个城市都位于Ox轴上。该国政府决定建立新的广播电台。经过多次实验,Berland科学家得出结论,在任何城市,公民的 不满 程度等于城市与电视台之间的距离乘以公民数量。在Ox轴上找到这样的点作为车站,以使 所有城市的不满总和 最小。

输入值

输入从整数正数N(0 <N <15000)  – Berland中城市数量的行开始 。接下来的 N 对 (X,  P) 描述了城市 (0 <X,P <50000),其中 X 是城市的坐标,  P 是公民的数量。所有数字均以空格分隔。

输出量

10-5的精度写出电视台的最佳位置 。

样本输入

4
1 3
2 1
5 2
6 2

样本输出

3.00000

用三分来做是可以的,因为函数f一定是凹函数。

代码:

#include<iostream>
#include<string.h>
using namespace std;
 
int list1[15001];
int list2[15001];
int n;
 
double f(double p)
{
	double sum = 0;
	for (int i = 0; i < n; i++)
	sum += list2[i] * ((list1[i]>p) ? list1[i] - p : p - list1[i]);
	return sum;
}
 
int main()
{	
	cin >> n;
	for (int i = 0; i < n; i++)cin >> list1[i] >> list2[i];
	double low = 0, high = 50000;
	double mid, mm;
	while (low + 0.000001 < high)
	{
		mid = (low + high) / 2;
		mm = (mid + high) / 2;
		if (f(mid) < f(mm))high = mm;
		else low = mid;
	}
	cout << mid;
	return 0;
}

实际上,这个问题要么有唯一解,要么有无穷解。

本题就是要找一个点,使得左边的人和右边的人一样多。

这个点可能是所给的某个点,也有可能是某2个相邻的点之间的线段上 任意一点都行。

也就是说,一定可以取所给的某个点作为答案。

但是这没什么卵用啊,那么多点,如果不排序的话就只能把每个点试一下,不管是哪种都不会很快。

 POJ 2366 Sacrament of the sum(两个数的和,二分or not二分)

题目:

Description

— The Brother of mine, the Head of Monastic Order wants to know tomorrow about the results long-term researches. He wants to see neither more nor less than the Summering Machine! Even moreover, he wants our Machine — only a machine — to demonstrate its comprehension of the Sacrament of the Sum as deeply as it is possible. He wants our Machine to find two numbers that give the sum equal to the Sacred Number 10 000. 
— Tsh-sh-sh! This is madness that borders on blasphemy! How can the Machine calculate the Sacred Number? Twenty seven years we work on it, but we've could teach it to tell if the sum of two introduced numbers greater or lower than 10 000. Can an ordinary mortal find two numbers that there sum will be equal to 10 000? 
— But we'll have to do it with the help of our Machine, even if it is not capable. Otherwise we'll have... let's say, big problems, if it is possible to call boiling oil like this. However, I have an idea. Do you remember, last week we've entered two numbers -7 and 13 into the Machine, and it answered that their sum is lower than 10 000. I don't know how to check this, but nothing's left for us than to believe to the fruit of our work. Let's enter now a greater number than -7 and start up the Machine again. We'll do like this again and again until we find a number that being added to 13 will give us 10 000. The only thing we are to do is to prepare an ascending list of numbers. 
— I don't believe in this... Let's start with the sum that is obviously greater than the Sacred Number and we'll decrease one of the summand. So we have more chances to avoid boilin... big problems. 

Haven't come to an agreement, the Brothers went away to their cells. By next day everyone of them has prepared a list of numbers that, to his opinion, could save them... Can both of the lists save them together? 
Your program should decide, if it is possible to choose from two lists of integers such two numbers that their sum would be equal to 10 000.

Input

You are given both of these lists one by one. Format of each of these lists is as follows: in the first line of the list the quantity of numbers Ni of the i-th list is written. Further there is an i-th list of numbers each number in its line (Ni lines).The following conditions are satisfied: 1 <= Ni <= 50 000, each element of the lists lays in the range from -32768 to 32767. The first list is ascending and the second one is descending.

Output

You should write "YES" to the standard output if it is possible to choose from the two lists of integers such two numbers that their sum would be equal to 10 000. Otherwise you should write "NO".

Sample Input

4
-175
19
19
10424
3
8951
-424
-788

Sample Output

YES

Hint

This problem has huge input data,use scanf() instead of cin to read data to avoid time limit exceed. 

这个题目,我觉得是不需要二分的,因为很明显有一个线性复杂度的解法。

虽然加上二分,如果写的非常6的话,可能会更快一点点,不过线性的算法应该可以认为是完美的了,毕竟存数组就需要theta(n)的时间。

代码:

#include<iostream>
using namespace std;
 
int main()
{
	int m,n;
	while (cin >> m)
	{
		int *listm = new int[m];
		for (int i = 0; i < m; i++)cin >> listm[i];
		cin >> n;
		int *listn = new int[n];
		for (int i = 0; i < n; i++)cin >> listn[i];
		int i = 0, j = 0;
		while (i < m&&j < n)
		{
			if ((listm[i] + listn[j])>10000)j++;
			else if ((listm[i] + listn[j]) == 10000)break;
			else i++;
		}
		if (i < m&&j < n)cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	return 0;
}

POJ 2785 Values whose Sum is 0(四个数的和,二分or not二分)

题目:

Description

The SUM problem can be formulated as follows: given four lists A, B, C, D of integer values, compute how many quadruplet (a, b, c, d ) ∈ A x B x C x D are such that a + b + c + d = 0 . In the following, we assume that all lists have the same size n .

Input

The first line of the input file contains the size of the lists n (this value can be as large as 4000). We then have n lines containing four integer values (with absolute value as large as 2  28 ) that belong respectively to A, B, C and D .

Output

For each input file, your program has to write the number quadruplets whose sum is zero.

Sample Input

6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45

Sample Output

5

Hint

Sample Explanation: Indeed, the sum of the five following quadruplets is zero: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

这个题目和上面的POJ 2366 Sacrament of the sum差不多,所以我的思路是把2个数组求和排序,另外2个数组也求和排序,转化成了两个数的和的问题。

我的代码:

#include<iostream>
#include<algorithm>
using namespace std;
 
int main()
{
	int n,m;
	while (cin>>n)
	{
		int *list1 = new int[n];
		int *list2 = new int[n];
		int *list3 = new int[n];
		int *list4 = new int[n];
		m = n*n;
		int *list5 = new int[m];
		int *list6 = new int[m];
		for (int i = 0; i < n; i++)cin >> list1[i] >> list2[i] >> list3[i] >> list4[i];
		for (int i = 0; i < m; i++)
		{
			list5[i] = list1[i%n] + list2[i / n];		
			list6[i] = -list3[i%n] - list4[i / n];
		}
		sort(list5, list5 + m);
		sort(list6, list6 + m);
		int i = 0, j = 0, sum = 0;
		while (i < m && j < m)
		{
			if (list5[i] < list6[j])i++;
			else if (list5[i] > list6[j])j++;
			else
			{
				int a = 1, b = 1;
				while (i < m&&list5[i + 1] == list5[i++])a++;//计算重数
				while (j < m&&list6[j + 1] == list6[j++])b++;
				sum += a*b;
			}
		}
		cout << sum << endl;
		delete list1, list2, list3, list4, list5, list6;
	}
	return 0;
}

其中,最后2个while循环是用来计算重复的数的,a和b相乘就是的了。

因为每个数都可以大到2的28次方,所以会有越界的情况。

于是我就把寻找list5[i]+list6[j]==0这种形式改成了寻找list5[i]==list6[j]这种形式。

刚好,这样的话,2个排序都可以用sort默认的自带的升序排序,不用自定义比较函数。

HDU 4071 Trick or Treat(三分,why?二分,wow!)

题目:

Johnny and his friends have decided to spend Halloween night doing the usual candy collection from the households of their village. As the village is too big for a single group to collect the candy from all houses sequentially, Johnny and his friends have decided to split up so that each of them goes to a different house, collects the candy (or wreaks havoc if the residents don't give out candy), and returns to a meeting point arranged in advance.There are n houses in the village, the positions of which can be identified with their Cartesian coordinates on the Euclidean plane. Johnny's gang is also made up of n people (including Johnny himself). They have decided to distribute the candy after everybody comes back with their booty. The houses might be far away, but Johnny's interest is in eating the candy as soon as possible.Keeping in mind that, because of their response to the hospitality of some villagers, some children might be wanted by the local authorities, they have agreed to fix the meeting point by the river running through the village, which is the line y = 0. Note that there may be houses on both sides of the river, and some of the houses may be houseboats (y = 0). The walking speed of every child is 1 meter per second, and they can move along any direction on the plane.At exactly midnight, each child will knock on the door of the house he has chosen, collect the candy instantaneously, and walk back along the shortest route to the meeting point. Tell Johnny at what time he will be able to start eating the candy.

InputEach test case starts with a line indicating the number n of houses (1 <=n <= 50 000). The next n lines describe the positions of the houses; each of these lines contains two oating point numbers x and y (-200 000 <= x; y <=200 000), the coordinates of a house in meters. All houses are at dierent positions. A blank line follows each case. A line with n = 0 indicates the end of the input; do not write any output for this case.OutputFor each test case, print two numbers in a line separated by a space: the coordinate x of the meeting point on the line y = 0 that minimizes the time the last child arrives, and this time itself (measured in seconds after midnight). Your answer should be accurate to within an absolute or relative error of 10  -5.Sample Input

2
1.5 1.5
3 0

1
0 0

4
1 4
4 4
-3 3
2 4

5
4 7
-4 0
7 -6
-2 4
8 -5

0

Sample Output

1.500000000 1.500000000
0.000000000 0.000000000
1.000000000 5.000000000
3.136363636 7.136363636

题意:在一个直角坐标系中,给出n个点。在 x 轴上选一个点,使得到那 n 个点的最长距离最短。

代码:

#include<iostream>
#include<math.h>
#include"stdio.h"
using namespace std;
 
int n;
double x[50001], y[50001];
 
double f(double a)
{
	double s = 0, temp;
	for (int i = 1; i <= n; i++)
	{
		temp = sqrt((a - x[i])*(a - x[i]) + y[i] * y[i]);
		if (s < temp)s = temp;
	}
	return s;
}
 
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)scanf("%lf %lf", &x[i], &y[i]);
	double low = -200000, high = 200000, mid1, mid2;
	while (low + 0.000001 < high)
	{
		mid1 = (low + high) / 2;
		mid2 = (mid1 + high) / 2;
		if (f(mid1) < f(mid2))high = mid2;
		else low = mid1;
	}
	printf("%.6f %.6f\n", mid1, f(mid1));
	return 0;
}

这个代码已经AC了,实际上题目有点问题,测试用例并没有多组,只有一组。

更大的问题是,这题为啥能三分呢?我怀疑可能只是数据不够严,所以这个不严谨的代码也过了。

网友二分的思路是对的:

假想所有点被一个圆包围着,那么题目就是要求这个最小的圆的半径和圆心坐标(在y轴上)。

对圆的半径进行二分,对于每一个半径,枚举每一个点。以每个点为圆心作圆,则必定在y轴上得到一个区间,求所有区间的交集。若交集为空,返回错误,否则取其中点为圆心并返回正确。

附上一个有趣的菜单:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值