【洛谷算法1-6】二分查找与二分答案

果真自己还是菜啊,二分模板背的比较熟悉了,做二分的题还是笨的摸不着边际。

P2249 【深基13.例1】查找 【标准的整数二分】

在这里插入图片描述
https://www.luogu.com.cn/problem/P2249

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e6+5;
int a[N];
int main(void)
{
	int n,m; cin>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	while(m--)
	{
		int l=0,r=n-1;
		int number; cin>>number;
		while(l<r)
		{
			int mid=l+r>>1;
			if(a[mid]>=number) r=mid;
			else l=mid+1;
		}
		if(a[l]==number) cout<<l+1<<" ";
		else cout<<"-1"<<" ";
	} 
	return 0;
}

P1102 A-B 数对 【哈希】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1102

#include<cstdio>
#include<iostream>
#include<map>
using namespace std;
const int N=2*1e7+20;
long long int a[N];
map<int,int>mp;//统计每个数的个数 
int main(void)
{
	int n,c; cin>>n>>c;
	for(int i=0;i<n;i++) cin>>a[i],mp[a[i]]++;
	long long int ans=0;
	for(int i=0;i<n;i++)
	{
		ans+=mp[a[i]-c];
	}
	cout<<ans<<endl;
	return 0;
}

P1873 砍树 【经典二分】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1873

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e6+10;
long long int a[N];
long long int n,m; 
bool check(int mid)
{
	long long int ans=0;
	for(int i=0;i<n;i++)
	{
		if(a[i]>mid) ans+=a[i]-mid;
	}
	if(ans>=m) return true;
	else return false;
}
int main(void)
{
	cin>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	int l=0,r=1e9;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l<<endl;
	return 0;
} 

P1024 [NOIP2001 提高组] 一元三次方程求解 【有意思 / 浮点数二分】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1024
方法一: 暴力
首先零点的左右两边的符号一定是不同的。

#include<cstdio>
#include<iostream>
using namespace std;
double a,b,c,d;
double f(double x)
{
	return a*x*x*x+b*x*x+c*x+d;
}
int main(void)
{
	cin>>a>>b>>c>>d;
	for(double i=-100;i<=100;i=i+0.01)
	{
		double l=i,r=i+0.01;
		if(f(i)*f(r)<0)//有根
		{
			double mid=(l+r)/2;
			printf("%.2lf ",mid);
		}
	}
	return 0;
} 

方法二: 标准的二分
提示很重要。
摘自: https://www.luogu.com.cn/problem/solution/P1024?page=6
在这里插入图片描述

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
using namespace std;
double a,b,c,d;
double f(double x){
    return (x*x*x*a+x*x*b+x*c+d);                   //f(x)表示将枚举的x带入后函数的数值 
}
int main(){
    cin>>a>>b>>c>>d;
    double x1,x2,mid;
    for (int x=-100;x<=100;x++){                    //暴力枚举所有有可能的根 
        x1=x,x2=x+1;
        if (f(x1)==0) printf ("%.2lf ",x1);         //恰好是整数根 
        else if (f(x1)*f(x2)<0){                    //由f(x1)*f(x2)<0很容易 得知真正的根在(x1,x2)之间 
            while (x2-x1>=0.001){                   //要求保留两位小数,为了保证结果正确,计算时精度要大于0.01 
                mid=(x1+x2)/2;                      //二分的思想,将实根的范围不断缩短直到小于0.001 
                if (f(x1)*f(mid)<=0) x2=mid;        //mid为中间值,根如果在(x1,mid) x2=mid;   缩短了根的范围 
                else x1=mid;
            }                                       //两根的范围已经符合要求退出循环 
            printf ("%.2lf ",x1);                   //输出结果 x1,(保留两位小数)空格别忘了! 
        }
    }
    return 0;
}

P1678 烦恼的高考志愿 【二分 / lower_bound 】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1678

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int a[100005];
int b[100005];
long long int sum;
int main(void)
{
	int n,m; cin>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<m;i++) cin>>b[i];
	sort(a,a+n);
	for(int i=0;i<m;i++)
	{
		int *temp=lower_bound(a,a+n,b[i]);//第一个大于等于b[i]的位置
		if(a[temp-a]==b[i]) continue;//如果相等那么不用加 
		if(a[temp-a]!=b[i]&&(temp-a)==0)//如果没有找到比b[i]小的位置 
		{
			sum+=abs(b[i]-a[0]);
			continue;
		}
		if(a[temp-a]!=b[i]&&(temp-a)==n) //如果没有找到比b[i]大的位置
		{
			sum+=abs(b[i]-a[n-1]);
			continue;
		}
		sum+=min(abs(a[temp-a]-b[i]),abs(a[temp-a-1]-b[i]));//向b[i]两边取较近的数 
	}
	cout<<sum<<endl;
	return 0;
} 

简化:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int a[100005];
int b[100005];
long long int sum;
int main(void)
{
	int n,m; cin>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<m;i++) cin>>b[i];
	sort(a,a+n);
	for(int i=0;i<m;i++)
	{
		int *temp=lower_bound(a,a+n,b[i]);//第一个大于等于b[i]的位置
		if(a[temp-a]==b[i]) continue;//如果相等那么不用加 
		if(temp-a==0)//如果没有找到比b[i]小的位置 
		{
			sum+=abs(b[i]-a[0]);
			continue;
		}
		if(temp-a==n) //如果没有找到比b[i]大的位置
		{
			sum+=abs(b[i]-a[n-1]);
			continue;
		}
		sum+=min(abs(a[temp-a]-b[i]),abs(a[temp-a-1]-b[i]));//向b[i]两边取较近的数 
	}
	cout<<sum<<endl;
	return 0;
} 

手写二分

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath> 
using namespace std;
int a[1000005];
int b[1000005];
int m,n;
long long int sum;
int main(void)
{
	cin>>m>>n;
	for(int i=0;i<m;i++) cin>>a[i];
	for(int i=0;i<n;i++) cin>>b[i];
	sort(a,a+m);
	for(int i=0;i<n;i++)
	{
		int l=0,r=m-1;
		while(l<r)
		{
			int mid=l+r>>1;
			if(a[mid]>=b[i]) r=mid;
			else l=mid+1;
		}
		if(a[l]==b[i]) continue;
		if(b[i]<=a[0]) {
			sum+=abs(b[i]-a[0]);
			continue;
		}
		if(b[i]>=a[m-1]){
			sum+=abs(b[i]-a[m-1]);
			continue;
		} 
		sum+=min(abs(b[i]-a[l]),abs(b[i]-a[l-1]));
	}
	cout<<sum<<endl;
	return 0;
}

P2440 木材加工

在这里插入图片描述
https://www.luogu.com.cn/problem/P2440

#include<cstdio>
#include<iostream>
using namespace std;
int a[100005];
int n,k;
bool check(int x)
{
	long long int ans=0;
	for(int i=0;i<n;i++)
		ans+=a[i]/x;
	if(ans>=k)  return true;
	else return false;
}
int main(void)
{
	cin>>n>>k;
	for(int i=0;i<n;i++) cin>>a[i];
	int l=0,r=1e9;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(check(mid)) l=mid;
		else  r=mid-1;
	}
	if(l) cout<<l<<endl;
	else cout<<0<<endl;
	return 0;
}

P1163 银行贷款 【浮点数二分】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1163

#include<cstdio>
#include<iostream>
using namespace std;
int a,b,c;
double check(double x)
{
	x=x/100;
	double sum=a;
	for(int i=1;i<=c;i++)
	{
		sum=sum*(1+x)-b;
	}
	return sum;
}
int main(void)
{
	cin>>a>>b>>c;
	double l=0,r=1000;
	while((r-l)>0.0000001)
	{
		double mid=(l+r)/2;
		if(check(mid)*check(l)<=0) r=mid;
		else l=mid;
	}
	printf("%.1lf",l);
	return 0;
}

P2678 [NOIP2015 提高组] 跳石头 【非常秒的整数二分】

在这里插入图片描述
https://www.luogu.com.cn/problem/P2678

话说,我好想懂得了二分的规律,看问题问的啥来确定边界。
思路摘自: ztc曾天成
我们的mid是最小距离。
思路:从起点出发,先选定一段距离mid,若前面的石头B与你所站着的石头A的距离小于mid(不符合,因为我们的mid应该是最下的),就把B搬掉,记录一下;如果不,就把B留下,再跳到石头B上。照这个步骤多次循环后,如果搬掉的石头多了,
就把距离mid定小点;如果少了,就把mid定大点。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+10;
int a[N];
int sum,n,m;
bool check(int x)
{
	int temp=0;
	int cnt=0;;
	for(int i=0;i<n;i++)
	{
		if(a[i]-temp<x) cnt++;//拿走
		else temp=a[i];//跳到下一个石头
	}
	if(sum-temp<x) cnt++;//最后到终点的位置
	if(cnt<=m) return true;
	else return false;//拿的少了
}
int main(void)
{
	cin>>sum>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	int l=0,r=1e9;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(check(mid)) l=mid;
		else r=mid-1; 
	}
	cout<<l<<endl;
	return 0;
}

P3853 [TJOI2007]路标设置 【有意思】

在这里插入图片描述
https://www.luogu.com.cn/problem/P3853

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e7+10;
int sum,n,m;
int a[N];
bool check(int x)
{
	int temp=0;
	long long int cnt=0;
	for(int i=1;i<n;i++)
	{
		if(a[i]-a[i-1]>x)//要添加路标
		{
			int t=(a[i]-a[i-1]);//计算要添加几个
			cnt+=t/x;
			if( (t%x) ==0) cnt--;//整除的话,减去多加的1次
		}
	}
	if(cnt<=m) return true;
	else return false;
}
int main(void)
{
	cin>>sum>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	int l=0,r=sum+1;
	while(l<r) 
	{
		int mid=l+r>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	cout<<l<<endl;
	return 0;
}

P1182 数列分段 Section II 【有意思】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1182

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N],n,m;
int l,r;
bool check(int x) 
{
	int cnt=0;
	int k=0;//记录分的段数 
	for(int i=0;i<n;i++)
	{
		if(cnt+a[i]>x)
		{
			cnt=0;
			k++;
		}
		cnt+=a[i]; 
	}
	if(k<m) return true;
	else return false;
}
int main(void)
{
 	cin>>n>>m;
	for(int i=0;i<n;i++)  cin>>a[i],r+=a[i],l=max(l,a[i]);
	while(l<r)
	{
		int mid=l+r>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	cout<<l<<endl;
	return 0;
}

P3743 kotori的设备 【有意思 / long double / check有意思】

在这里插入图片描述
https://www.luogu.com.cn/problem/P3743

注意用 long double 不然有一个点过不去

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

const int N=1e5+10;

struct node
{
	int a,b;//a是花费,b是当前电 
}Node[N];
int n,p;

bool check(long double x)
{
	double sum=p*x;//总的充电量
	for(int i=0;i<n;i++)
	{
		double temp=Node[i].a*x;
		if(temp>Node[i].b) sum=sum+Node[i].b-temp;//要充电
	}
	if(sum>0) return true;//冲的电够用
	else return false;//冲的不够用
}
int main(void)
{
	cin>>n>>p;
	long long int sum=0;
	for(int i=0;i<n;i++) cin>>Node[i].a>>Node[i].b,sum+=Node[i].a;
	if(sum<=p)//消耗量小于等于冲的量,可以无限工作
	{
		cout<<-1<<endl;
		return 0;
	}
	long double l=0,r=1e10;
	while(r-l>1e-9)
	{
		long double mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%llf\n",l);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值