二分 三分专题

本文详细介绍了二分与三分算法的应用。通过分析不同问题,如最大最小距离、二次函数优化和热膨胀问题,展示了如何利用二分和三分解决具有单调性和凹凸性的问题。此外,还讨论了在实际编程中需要注意的精度和循环条件等细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题面

I - 二分 HYSBZ - 1734
农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,…,xN (0 <= xi <= 1,000,000,000). 但是,John的C (2 <= C <= N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢

第一行:空格分隔的两个整数N和C

第二行—第N+1行:i+1行指出了xi的位置

第一行:一个整数,最大的最小值

Sample Input
5 3
1
2
8
4
9

Sample Output
3

(把牛放在1,4,8这样最小距离是3 )

分析

要使最小值最大,答案具有单调性,可以二分答案转化为判定
在这里用的二分是闭区间 [ l,r ]
特别注意一点,这里要用 mid=(l+r+1)>>1 而不能 mid=(l+r)>>1
当 l r 相差1时,即 l+1=r 时,mid=(l+r)>>1 向下取整 = l
执行分支 l=mid =l 会导致死循环,因为区间[ l,r ] 没有缩小,一直停留在 l+1==r
执行分支 r=mid-1 =l-1 会以 l>r 结束, 而不是 l=r
而用 mid=(l+r+1)>>1 则可以避免 mid 向下取整时取到左端点 l

代码


#include<iostream>
using namespace std;
#include<stdio.h>
#include<algorithm> 

int N,C;
const int MAX_N=1e5+5;
int a[MAX_N];

bool check(int d)
{
	int pre=a[0],cnt=1;			//第一个必放 
	for(int i=1;i<N;i++){
		if(a[i]>=pre+d) { 
			pre=a[i]; cnt++;
			if(cnt>=C) return 1;
		}
	}
	return 0;
}

int bisearch(int l,int r)
{
	while(l<r){
		int mid=(l+r+1)>>1;			 特别注意 向下取整 避免取到 l  
		if(check(mid)) l=mid;
		else r=mid-1;			//
	}
	//printf("l=%d r=%d\n",l,r);			//l==r时结束 
	return l;
}

int main()
{
	cin>>N>>C;
	for(int i=0;i<N;i++) cin>>a[i];	
	
	sort(a,a+N);
	cout<<bisearch(1,a[N-1]-a[0])<<endl;
	
	
	return 0;
 } 

题面

K - 三分 HDU - 3714
Josephina is a clever girl and addicted to Machine Learning recently. She
pays much attention to a method called Linear Discriminant Analysis, which
has many interesting properties.
In order to test the algorithm’s efficiency, she collects many datasets.
What’s more, each data is divided into two parts: training data and test
data. She gets the parameters of the model on training data and test the
model on test data. To her surprise, she finds each dataset’s test error curve is just a parabolic curve. A parabolic curve corresponds to a quadratic function. In mathematics, a quadratic function is a polynomial function of the form f(x) = ax2 + bx + c. The quadratic will degrade to linear function if a = 0.
在这里插入图片描述
It’s very easy to calculate the minimal error if there is only one test error curve. However, there are several datasets, which means Josephina will obtain many parabolic curves. Josephina wants to get the tuned parameters that make the best performance on all datasets. So she should take all error curves into account, i.e., she has to deal with many quadric functions and make a new error definition to represent the total error. Now, she focuses on the following new function’s minimum which related to multiple quadric functions. The new function F(x) is defined as follows: F(x) = max(Si(x)), i = 1…n. The domain of x is [0, 1000]. Si(x) is a quadric function. Josephina wonders the minimum of F(x). Unfortunately, it’s too hard for her to solve this problem. As a super programmer, can you help her?
Input
The input contains multiple test cases. The first line is the number of cases T (T < 100). Each case begins with a number n (n ≤ 10000). Following n lines, each line contains three integers a (0 ≤ a ≤ 100), b (|b| ≤ 5000), c (|c| ≤ 5000), which mean the corresponding coefficients of a quadratic function.
Output
For each test case, output the answer in a line. Round to 4 digits after the decimal point.
Sample Input
2
1
2 0 0
2
2 0 0
2 -4 2
Sample Output
0.0000
0.5000

题面描述

定义一个新函数F(x),为所有f(x)的最大值,求F(x)的最小值

分析

二分是针对具有单调性的问题
而三分则是针对具有凹凸性的问题 (且要求函数图像两边严格单调)
在此所有f(x)为开口向上的二次函数,而其最大值组成的F(x)也是一个凹函数
即求凹函数的最小值,可以用三分

这里用lmid和rmid表示(l,r)内的三等分点
注意一下精度问题,时间要求不高的话直接用100次循环来缩小范围

代码


#include<iostream>
using namespace std;
#include<algorithm> 

const int INF=2e9;
const int MAX_N=1e4+5;
int n;
double a[MAX_N],b[MAX_N],c[MAX_N];	

double f(double x){
	double ans=-INF;
	for(int i=0;i<n;i++) ans=max(ans, a[i]*x*x+b[i]*x+c[i] );
	return ans;
}

void bisearch(double l,double r)
{
	for(int i=0;i<100;i++){	
		double lmid=l+(r-l)/3 ,rmid=r-(r-l)/3 ;		//三等分点 
	
		if(f(lmid)<f(rmid)) r=rmid;		// a>0 必为凹函数 
		else l=lmid;
	}
	
	printf("%.4f\n",f(l));
}

int main()
{
	int t;cin>>t;
	while(t--){
		cin>>n;
		for(int i=0;i<n;i++) cin>>a[i]>>b[i]>>c[i];	
		
		bisearch(0,1000);	
	}	
	
	return 0;
 } 

题面

J - 二分 POJ - 1905
在这里插入图片描述
When a thin rod of length L is heated n degrees, it expands to a new length L’=(1+n*C)*L, where C is the coefficient of heat expansion.
When a thin rod is mounted on two solid walls and then heated, it expands and takes the shape of a circular segment, the original rod being the chord of the segment.

Your task is to compute the distance by which the center of the rod is displaced.
Input
The input contains multiple lines. Each line of input contains three non-negative numbers: the initial lenth of the rod in millimeters, the temperature change in degrees and the coefficient of heat expansion of the material. Input data guarantee that no rod expands by more than one half of its original length. The last line of input contains three negative numbers and it should not be processed.
Output
For each line of input, output one line with the displacement of the center of the rod in millimeters with 3 digits of precision.
Sample Input
1000 100 0.0001
15000 10 0.00006
10 0 0.001
-1 -1 -1
Sample Output
61.329
225.020
0.000

分析

这里可以直接二分长度,也可以二分角度
根据几何关系计算一下就ok了

实数域的二分,特别注意精度问题 eps至少要设置为1e-8才能过

代码


#include<iostream>
using namespace std;
#include<stdio.h>
#include<math.h>

const double EPS=1e-8;			/

int main()
{
	double L,n,C;
	//double x;
	while(cin>>L>>n>>C && L>=0&&n>=0&&C>=0){
		
		double LL=(1+n*C)*L;
		
		double lb=0,ub=L/2;		//	x  变形幅度最大不超过 r/2   
		while(ub-lb>EPS){		//
			double mid=(lb+ub)/2;
			double r=(mid*mid +L*L/4)/2/mid;		//mid
			double L2=asin(L/2/r)*2*r;
					
			//double al=2*atan(2*mid/L);	//alpha
			//double L2=L*(al/sin(al));
			
			if(L2<LL) lb=mid;			//  == 
			else  ub=mid;
		}
		printf("%.3f\n",lb);		
	}
	
	return 0;
}

题面

H - 二分+交互 Gym - 101375H
Obs: this is an interactive problem. More information is under the “Interaction” section.

MaratonIME is gathering to start another group practice. This time, Renzo decided to reward the students with candies as they solve problems. Curious as they are, the members of MaratonIME started to guess how many candies did Renzo bring. For each question, Renzo answered if the amount of candies was higher, lower or equal to the number asked.

Breno, noticing that the amount of candies could be very high, decided to limit the number of queries to 50. This way, the practice would start as soon as possible.

Renzo bought at least 1 and no more than 109 candies. Find how many candies were bought by Renzo with no more than 50 questions.

Input
For every question asked, read a character. It will be " > " if the amount of Renzo’s candies is higher than your guess, " < " if the amount of Renzo’s candies is lower than your guess or " = " if your guess is equal to the amount of Renzo’s candies.

Output
You must print every query in the format “Q y”, where y is the number guessed, and 1 ≤ y ≤ 109. After printing a question, you need to flush the output. Check the “Interaction” section for examples of how to flush the output.

Interaction
To ask Renzo a question, print the query in the format above. After printing a question, you need to flush the output. See examples below for each language:

C: fflush(stdout)

C++: cout.flush()

Java: System.out.flush()

Python: sys.stdout.flush()

Pascal: flush(output)

After each query, read a character as described above.

As soon as Renzo answers your question with “=” the program has to terminate. Your answer will be considered correct if you asked no more than 50 questions.

Example
Input

<

Output
Q 1
Q 3
Q 2
Note
In the example, the number of candies is 2. The answer is considered to be correct because only 3 out of the 50 possible questions were asked to obtain the character " = ".

Make sure to flush the output after printing each question!

分析

交互题
先输出后输入
注意循环条件不能用 l<r
否则当 l==r 时,还没回答=,就已经出循环了

代码

#include<iostream>
using namespace std ;

char ch;

void bisearch(int l,int r)
{
	while(1){		//不能用 l<r 否则到不了输出'=' 
		int mid=(l+r)>>1;	///
		cout<<"Q "<<mid<<endl;
		
		cout.flush();		/每次询问后刷新
		cin>>ch;
		getchar();			
		
		if(ch=='<') r=mid-1;
		else if(ch=='>') l=mid+1;		
		else if(ch=='=') return ;		
	}
}

int main()
{
	bisearch(1,1e9);
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值