二分和二分答案和三分(经典例题)

二分和二分答案和三分(经典例题)

二分模板

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。

【样例解释】

img

如图所示,红色段即为该函数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;
}

猜你喜欢

转载自blog.csdn.net/hhuhgfhggy/article/details/109323036