二分与三分(分治)

二分与三分(分治)
[整数二分]
写法1(求最小值最大):

int l=1,r=n;
while(l<r){         //                       答案可行区间 
	int mid=(l+r+1)/2;//加1修正          |--------------------|
	if(check(mid)){//mid成立,舍去左边。 L|______________m_______________|R
		l=mid;
	}else{      //   mid不成立,舍去右边。  答案可行区间      
		r=mid-1;//                       |------------|
	}           //                      L|______________m_______________|R
}

写法2(求最大值最小):

int l=1,r=n;
while(l<r){
	int mid=(l+r)/2;//                                 答案可行区间 
	if(check(mid)){//mid成立,舍去右边。            |-------------------|
		r=mid;     //                   L|______________m_______________|R 
	}else{         //mid不成立,舍去左边。                   答案可行区间 
		l=mid+1;   //                                      |------------|
	}              //                   L|______________m_______________|R
}

[实数二分]
(double)

double l,r,eps=1e-7;//eps为精度 
while(r-l>eps){
	if(check(mid)){//或 
		l=mid;     //r=mid
	}else{         //
		r=mid;     //l=mid
	}
}

二分例题:
1.[数列分段]
accoders的2046【一本通提高篇二分与三分】 数列分段II
luogu 的 P1182 数列分段 Section II

思路:
求最大值最小,运用[整数二分]写法2,l为数组中最大,
r为total.

code:

#include<bits/stdc++.h>
using namespace std;
int a[1000010],n,m; 
int check(int x){
	int sum=0,ans=1;//ans为当前有几组,sum为当前组总和 
	for(int i=1;i<=n;i++){
		if(sum+a[i]<=x){
			sum+=a[i];
		}else{
			/* 当你把l设为0,r设为1e9时: 
			if(a[i]>x){
				return 0;
			}
			*/
			ans++;
			sum=a[i];
		}
	}
	return ans<=m;
}
int main(){
	cin>>n>>m;
	int l=0,r=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		l=max(l,a[i]);//l为最大值 
		r+=a[i];      //r为total 
	}
	while(l<r){
		int mid=l+r>>1;
		if(check(mid)){
			r=mid;
		}else{
			l=mid+1;
		}
	}
	cout<<l<<"\n";
	return 0;
}

2.[河中跳房子]
accoders的 1902【一本通基础分治】河中跳房子
思路:
求最小值最大化,[整数二分]写法1,二分跳跃长度,
l为最小间距,r为L。
code:

#include<bits/stdc++.h>
using namespace std;
int L,n,m,a[50010],l=2e9,r=2e9;
int check(int x){
	int ans=0,s=0;
	for(int i=1;i<=n+1;i++){
		if(a[i]-s>=x){
			s=a[i];
		}else{
			ans++;
		}
	}
	return ans<=m;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>L>>n>>m;
	r=L;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		l=min(l,a[i]-a[i-1]);
	}
	l=min(l,L-a[n]);
	a[n+1]=L;
	while(l<r){
		int mid=(l+r+1)/2;
		if(check(mid)){
			l=mid;
		}else{
			r=mid-1;
		}
	}
	cout<<l;
	return 0;
}

3.[愤怒的牛]
accoders的【一本通提高篇二分与三分】 愤怒的牛 2043
luogu的 P1824 进击的奶牛
思路:
求最小值最大化,[整数二分]写法1,二分牛的间隔距离,
l为最小间距,r为第一牛棚与最后牛棚的间距。
code:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[100010],l=2e9,r;
int check(int x){
	int ans=1,s=1;
	for(int i=2;i<=n;i++){
		if(a[i]-a[s]>=x){
			ans++;
			s=i;
		}
	}
	return ans>=m;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	sort(a+1,a+n+1);
	for(int i=2;i<=n;i++){
		l=min(l,a[i]-a[i-1]);
	}
	r=a[n]-a[1];
	while(l<r){
		int mid=l+r+1>>1;
		if(check(mid)){
			l=mid;
		}else{
			r=mid-1;
		}
	}
	cout<<l;
	return 0;
}

4.[Best Cow Fences]
accoders的【一本通提高篇二分与三分】 Best Cow Fences 2044
luogu的 P10450 [USACO03MAR] Best Cow Fences G
思路:
求长度至少为L的子串的平均值最大,[实数二分],l为0.0,r为total。
check运用前缀和求平均。
code:

#include<bits/stdc++.h>
using namespace std;
int a[100010];
int n,m;
double l=0.0,r,eps=1e-5,b[100010];
int check(double x){
	for(int i=1;i<=n;i++){
		b[i]=(b[i-1]+a[i]*1.0-x)*1.0;
	}
	double ans=0;
	for(int i=m;i<=n;i++){
		ans=min(ans,b[i-m]);
		if(b[i]-ans>=0.0){
			return 1;
		}
	}
	return 0;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		r+=a[i];
	}
	while(r-l>eps){
		double mid=(l+r)/2.0;
		if(check(mid)){
			l=mid;
		}else{
			r=mid;
		}
	}
	cout<<int(r*1000);
	return 0;
}

5[木材加工]
accoders 的 木材加工9052
思路:
[整数二分]写法1的板子,没啥好说的。
code:

#include<bits/stdc++.h>
using namespace std;
int a[100010];
int n,k,l=0,r=-1;
int check(int x){
	int ans=0;
	for(int i=1;i<=n;i++){
		ans+=a[i]/x;
	}
	return ans>=k;
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		r=max(r,a[i]);
	}
	while(l<r){
		int mid=l+r+1>>1;
		if(check(mid)){
			l=mid;
		}else{
			r=mid-1;
		}
	}
	cout<<l;
	return 0;
}

[三分]
找单峰
模版code:

#include<bits/stdc++.h>
int f[100010];//f为差分数组
int sf(int l,int r){
	while(l<=r){
		int mid1,mid2;
		mid1=(2*l+r)/3;
		mid2=(2*r+l)/3;
		if(f[mid1]<f[mid2]){
			r=mid2-1;
		}else{
			l=mid1+1;
		}
	}
}

1.accoders【一本通提高篇二分与三分】 曲线 2045
luogu P1883 【模板】三分 | 函数
code:

#include<bits/stdc++.h>
using namespace std;
int n;
double a[100010],b[100010],c[100010],eps=1e-11;
double check(double x){
	double mx=-1e9;
	for(int i=1;i<=n;i++){
		mx=max(mx,a[i]*x*x+b[i]*x+c[i]);
	}
	return mx;
}
int main(){
	int T;
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i]>>b[i]>>c[i];
		}
		double l=0,r=1000;
		while(r-l>eps){
			double mid1=l+(r-l)/3,mid2=r-(r-l)/3;
			if(check(mid1)<=check(mid2)){
				r=mid2;
			}else{
				l=mid1;
			}
		}
		printf("%.4lf\n",check(r));
	}
	return 0;
}

2.accoders的【一本通提高篇二分与三分】 灯泡 2048
luogu的 P5931 [清华集训2015] 灯泡
code:

#include<bits/stdc++.h>
using namespace std;
int t;
double hb,h,d;
double check(double x){
	return d-x+hb-(d*hb-d*h)/x;
}
int main(){
	cin>>t;
	while(t--){
		cin>>hb>>h>>d;
		double l=d-h*d/hb,r=d;
		while(r-l>1e-9){
			double mid1=l+(r-l)/3.0,mid2=r-(r-l)/3.0;
			if(check(mid1)<=check(mid2)){
				l=mid1;
			}else{
				r=mid2;
			}
		}
		printf("%.3lf\n",check(r));
	}
	return 0;
}
posted @ 2025-01-17 08:01  lbh123  阅读(45)  评论(0)    收藏  举报