整数二分(笔记)

二分是我学的最晕的地方之一了,为此特地过来记一个笔记。

这里只是一个笔记而已并不是全面讲解整数二分
二分的原理:

二分的原理实际上就是类似于猜答案,首先知道一个答案的范围,然后每次都用这个范围的中点去试探答案,然后根据结果去不断的缩小范围逼近答案。

整数二分:

模板:

int besearch_1(ll l,ll r)
{
	ll mid;
	while(l < r)
	{
	    mid = r + l >> 1;
		if(check(mid))
			r = mid;
		else
		    l = mid + 1;
	}
	return l;
}
int besearch_1(ll l,ll r)
{
	ll mid;
	while(l < r)
	{
	    mid = r + l + 1>> 1;
		if(check(mid))
			l = mid;
		else
		    r = mid - 1;
	}
	return l;
}
题目一:

题目
意思是给一个排好序的序列(升序), 要求找到一个数输出这个数在这个序列的范围,比如
1 , 2 , 4 , 4 , 4 , 8 , 9 1,2,4,4,4,8,9 1,2,4,4,4,8,9
要找4这个数,那么就输出2和4,要找2就输出1和1,要找7就输出-1和-1(因为没有7这个数)
那么这道题就可以用二分去做
拿查找4这个数来举例(用数组q来储存长度为n的序列)

1.首先要先确定该数所在的范围,4这个数所在的范围是0和6之间,用l和r表示即l = 0, r = 6(整个序列的长度);
2.然后取中间值mid = l + r >> 1;
3.接下来我们对mid进行检查,我们想要确定4这个数就要找它的边界,就拿左边界来说,我们很容易发现4的左边界右边全部大于等于4,右边全部小于4,于是我们可以得到这个结果:
1.当q[mid] >= 4,那么此时mid可能包含左边界,也可能大于左边界,此时我们用mid替换r
2.当q[mid] < 4, 那么此时mid一定不包含边界而且小于边界此时用mid替换掉l + 1
即:

if(q[mid] >= 4)
	r = mid;
else
	l = mid + 1;

这明显和第一个模板相似,所以套用第一个模板。右边界也同样判断。

题目二:

数列分段
题面中文不再解释,首先要考虑要二分什么,对于这道题,直接二分所求的答案就好了。
1.确定范围
答案最小可能是所有数中最小的一个,这应该是答案最小情况,即左边界 l l l的值,又边界的话就是所有值之和了(只分成了一组),明白了这个之后,就是check函数的思路了。

2.check函数
check函数要判断什么呢,这一点我迷惑了很久,其实后来才发现,其实对于一个mid值他只有两种情况而已,要么能把整个序列分成k段而且最大值为mid,要么不能,而对于满足能分的mid值自身也有一个范围,而这个题实际上就是求这个范围的左边界罢了。

#include<bits/stdc++.h>
#define eps 1e-8
#define ll long long
#define INF 0X3f3f3f3f
using namespace std;
const ll maxn = 1e6 + 5;
ll n,num[maxn],k,l,r;
int check(ll mid)
{
    ll temp = 0,cnt = 0;
    //贪心思想,从左向右按照不超过mid的原则去划分区间,
    //得到的cnt就是区间数,如果这个区间数比要划分的k还要大的话
    //说明要合并一部分区间达到k个区间,那么此时就会超过mid所以此时的mid一定不满足
    //反之可能满足
    for(int i = 0; i <= n; i++)
    {
        if(num[i] + temp <= mid)
            temp += num[i];
        else
            cnt++,temp = num[i];
    }
    return cnt > k;
}
int main()
{
    cin >> n >> k;
    num[n] = INF;
    for(int i = 0; i < n; i++)
        cin >> num[i],r = r + num[i],l = max(l,num[i]);
    while(l < r)
    {
        ll mid = l + r >> 1;
        if(check(mid))
            l = mid + 1;
        else
            r = mid;
    }
    cout << l << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值