二分 算法

个人的理解:众多周知,二分是一分为二。二分分为 二分查找 和 二分答案 。二分查找的话会更加容易理解一些,直接利用二分进行寻找要找的答案;二分答案是利用二分间接的寻找要求的结果。

同样的,二分 分为 整数二分 ,浮点二分 。

此外,二分的话要特别注意边界的判断,判断错的话可能会出现 可能会超时 ,运行错误 的问题。

自己积累的模板

习题

一:二分查找的模板题

789. 数的范围

题目大意

n个数,q次询问,给出要查询的数,问该数的第一次出现的位置和最后一次出现的位置,未找到则输出 -1 -1

解题思路

1.思考要用到的二分的边界

2.思考二分中间的范围变换应该是怎样的

坑点

1.无

算法1:二分

实现步骤

1.读入后,m次循环判断 左右边界的变化

2.最后判断要输出的是哪个端点

3.最后输出结果

代码 

#include<bits/stdc++.h>
using namespace std;
int n,q,k;
const int N=1e5+10;
int a[N];
int main()
{
	cin>>n>>q; 
	for(int i=0;i<n;i++)
	{
		cin>>a[i];//依次读入 n 个数  
	}
	while(q--)//q 次询问 
	{
		cin>>k;//读入要查询的数 
		int l=0,r=n-1;//左右端点的范围 
		while(l<r)//左端点 
		{
			int mid = l + r >> 1;//中点 
			if(a[mid]>=k)//超出左端点的范围 
			{
				r=mid;//将右端变为 mid 进行缩小范围 ,继续判断 
			}
			else//未达到左端点 
			{
				l=mid+1;//将 l 变为 mid +1 进行扩大范围,继续判断 
			} 
		}
		if(a[l]!=k)//未找到左端点 
		{
			cout<<"-1 -1";//直接输出两个 -1  
		}
		else//找到左端点 
		{
			cout<<l<<" ";//输出左端点 
			l=0,r=n-1;//左右端点的范围 
			while(l<r)
			{
				int mid= l + r +1 >>1;//为了避免死循环 
				if(a[mid]<=k)//右端点 
				{
					l=mid;//不满足右端点,要向右扩大范围,进行判断 
				}
				else{
					r=mid-1;//mid 超出范围 ,利用 r= mid -1 进行缩小范围  
				}
			}
			cout<<l<<endl;
		}
		
	}
	return 0;
} 

二:二分答案

P7585 [COCI2012-2013#1] LJUBOMORA

题目大意

给你孩子数,弹珠种类数,让你求出最小的嫉妒值

解题思路 

1.首先看到题目感觉和二分没什么联系,但看一眼题目标签发现有二分

2.接着判断是查找还是答案

3.判断二分的边界,范围的变化

坑点

1.题中数的范围较大,所以要开 long long

算法:二分答案

实现步骤 

1.根据二分答案的模板,所以需要一个函数进行检测是否符合二分的要求

2.然后再主函数里直接利用该函数进行检测中点是否符合题中要求

3.接着判断二分的范围变化

4.最终输出结果

 代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 300000 + 10;
ll n, m, l, r, ans, mid, a[N];//根据题意 ,数据需要开 longlong 
bool check(ll x)//利用 check 函数  求出每种颜色可得人数 
{
	ll sum = 0;//用 sum 来记录 
	for(int i=1;i<=m;i++)
	{
		sum += a[i] / x;//记录 人数 
		if(a[i] % x != 0)//有余数的情况 
		{	
			sum++;//需要另+1 ,因为该题符合向上取整 
    	}
	}
	return sum <= n;//人数是否达标
}

int main()
{
	cin >> n >> m;//读入n个孩子 ,m 种弹珠 
	for(int i=1;i<=m;i++)
	{
		cin >> a[i];
		r += a[i];//确定右边界,嫉妒值极限为所有球之和
	}
	while(l <= r)
	{//开始二分
		mid = (l + r) >> 1;//中间 值 >> 等价于 /2 ,因为 + 的优先级高于 >> 所以无需加括号 
		if(check(mid))
		{                        //返回true意味着可以考虑更大些
			r = mid - 1;//返回 true意味着要大些  表示  mid>=要求的点,向左移动 ,缩小范围 
			ans = mid;//将mid 赋给 ans 
		}else//相反 mid <要求的点 
		{
			l = mid + 1;//返回false意味着要小些   需要向右移动,扩大范围 
		}	
	}
	cout << ans << endl;//输出答案
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值