二分和二分答案和三分(经典例题)
二分模板
int find_low(int x)
{
int mid,l=1,r=n;
while(l<=r)
{
mid=(l+r)/2;
if(a[mid]<x)
l=mid+1;
else
r=mid-1;
}
return l;
}
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
在从小到大的排序数组中,
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
一、求和
描述
给你N行4列的数字,从每一列选一个数,问使他们的和为0的情况有多少种?
输入
第一行包括一个整数N,表示这四个序列的长度
接下来N行每行包括四个用空格分隔的整数,四个数分别表示四个序列的第i个数。
输出
输出一行,表示满足条件的种数。
输入样例 1
2
1 4 -1 3
2 1 -3 -2
输出样例 1
2
输入样例 2
4
1 2 3 -2
-1 -3 0 -1
2 2 4 0
3 -2 -5 -2
输出样例 2
17
提示
对于样例1:
1 + 4 + (-3) + (-2) = 0
2 + 1 + (-1) + (-2) = 0
一共有两种方式,则答案输出:2
对于100%的数据:1≤N≤400
对于序列中每一个数 − 1 0 9 −10^9 −109≤ a i a_i ai≤ 1 0 9 10^9 109
#include<bits/stdc++.h>
using namespace std;
long long a[5000],b[5000],c[5000],d[5000],x[5000],y[5000];
long long sum=0;
int n;
int main()
{
int k=1;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i]>>b[i]>>c[i]>>d[i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
x[k++]=a[i]+b[j];//将第一列的每一个数和第二列的每个数都相加,即将第一列和第二列相加的所有情况放入x数组;
}
}
k=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
y[k++]=c[i]+d[j];//将第三列的每个数和第四列的每个数都相加,即将第三列和第四列相加的所有情况放入y数组;
}
}
sort(y+1,y+k);
//然后依次遍历x,在y中二分查找是否有-x存在;
for(int i=1;i<k;i++)
{
int now = -x[i];
sum +=upper_bound(y+1,y+k,now) - lower_bound(y+1,y+k,now);//大于 now的第一个数减去大于等于now的第一个数,就是等于now的个数
}
cout<<sum<<endl;
return 0;
}
二、二分答案
对于难以直接确定解的问题,采取二分枚举+检验的思想将求解类问题转换为验证类问题
• >=K 可行
• <K 不可行
• 目标就是求K
一、牛棚(二分答案经典)
• 有N个牛棚在x轴上,已知他们的坐标.你有C只奶牛,
每只都必须安排在一个牛棚里,一个牛棚只能容纳一只.
但是他们会互相攻击,所以要求距离最近的两个牛棚间的距离最大.
• 2 <= N <= 100,000
• 0 <= xi <= 1,000,000,000
• 2 <= C <= N
Sample Input
5 3
1
2
8
4
9
Sample Output
3
#include<bits/stdc++.h>
using namespace std;
int n,c;
long long x[100005];
int judge(int a)
{
int t=x[1],sum=1;
for(int i=2;i<=n;i++)
{
if(x[i]-t>=a)
{
sum++;
t=x[i];
}
}
if(sum<c)
return 0;
else
return 1;
}
int main()
{
cin>>n>>c;
for(int i=1;i<=n;i++)
{
scanf("%d",&x[i]);
}
sort(x+1,x+n+1);
int l=1;//两头牛最小的距离;
int r=x[n];//可能的最大距离;
int mid;
while(l<=r)
{
mid=(l+r)/2;
if(judge(mid))//判断该答案是否可行
l=mid+1;
else
r=mid-1;
}
cout<<r<<endl;
return 0;
}
二、晾衣服(二分答案)
• 有n件衣服需要晾干,每件含水量 a i a_i ai.每件衣服每分钟自然干1单位的水.
每分钟可以对其中任意 一件使用吹风机,其可以减少k的水.
求晾干所有衣服的最少时间.
• 1 ≤ n ≤ 100 000
• 1 ≤ a i a _i ai ≤ 1 0 9 10^9 109
• 1 ≤ k ≤ 1 0 9 10^9 109
Sample Input
sample input #1
3
2 3 9
5
sample input #2
3
2 3 6
5
Sample Output
sample output #1
3
sample output #2
2
#include<stdio.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
int n,k;
long long x[100005];
int judge(int a)
{
int sum=0;
for(int i=1;i<=n;i++)
{
if(x[i]>a)
{
sum+=ceil((x[i]-a)*1.0/(k-1)); //如果在mid时间内干不了,就得用吹风机。该式子表示需要用吹风机几分钟
}
//(k-1)是因为,k里面有1是自然风干的。
if(sum>a)//如果需要吹风机的时间大于a就说明该答案不行
return 0;
}
return 1;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&x[i]);
}
cin>>k;
sort(x+1,x+n+1);
if(k==1){printf("%d",x[n]); return 0;}//特例
int l=1;//用的最短时间;
int r=x[n];//用的最长时间;
int mid;
int ans=x[n];//记录答案
while(l<=r)
{
mid=(l+r)/2;
if(judge(mid))//判断该答案是否可行
r=mid-1,ans=mid;
else
l=mid+1;
}
cout<<ans;
return 0;
}
三、三分
类似二分的定义Left和Right
mid = (Left + Right) / 2
midmid = (mid + Right) / 2;
如果mid靠近极值点,则Right = midmid;
否则(即midmid靠近极值点),则Left = mid;
三分模板题
题目描述
如题,给出一个 N 次函数,保证在范围 [l,r]内存在一点 x,使得 [l,x] 上单调增,[x,r] 上单调减。试求出 x 的值。
输入格式
第一行一次包含一个正整数 N 和两个实数 l,r,含义如题目描述所示。
第二行包含N+1 个实数,从高到低依次表示该 N 次函数各项的系数。
输出格式
输出为一行,包含一个实数,即为x 的值。四舍五入保留 5 位小数。
输入输出样例
输入 #1
3 -0.9981 0.5
1 -3 -3 1
输出 #1
-0.41421
说明/提示
对于 100% 的数据,7≤N≤13。
【样例解释】
如图所示,红色段即为该函数f(x) = x^3 - 3 x^2 - 3x + 1 在区间 [−0.9981,0.5] 上的图像。
当 x=−0.41421 时图像位于最高点,故此时函数在 [l,x] 上单调增,[x,r] 上单调减,故 x=−0.41421 ,输出 −0.41421。
(Tip.l&r的范围并不是非常大ww不会超过一位数)
//https://www.luogu.com.cn/problem/P3382
#include<bits/stdc++.h>
using namespace std;
int N;
double l,r;
double arr[20];
double fun(double x);
int main()
{
scanf("%d%lf%lf",&N,&l,&r);
for(int i=1;i<=N+1;i++) scanf("%lf",&arr[i]);
while(fabs(r-l)>=1e-6){
double mid = (l+r)/2;
double midmid = (l+mid)/2;
double resr = fun(mid);
double resl = fun(midmid);
if(resl<resr) l=midmid;
else r=mid;
}
printf("%.5lf",l);
return 0;
}
double fun(double x)
{
double res=0;
for(int i=N+1;i>=1;i--)
res+=arr[i]*pow(x,N-i+1);
return res;
}