Kevin的算法笔记(1) 二分

二分是一个用于在序列中查找特定元素的方法,可以用二分法来解决问题的条件是:如果某一元素满足给定的条件,则它的左边(或右边)都会满足条件,因此能被二分的序列一定有顺序。根据中点是否满足条件,不断更新两个端点,每次排除一半的答案,直到找到最值,这是二分查找的基本思路。C++内置了有关二分查找的两个函数std::lower_bound和std::upper_bound。

除此之外,有一种思想叫做二分答案。当我们在解题时,常常需要枚举答案,这时候按照二分的方法枚举能大大降低时间复杂度。能用二分答案的前提是答案满足与二分查找中类似的单调性。

二分查找的时间复杂度是o(logn)。

下面我们用例题来理解一下二分的思路。

 
在这道题中,如果我们想直接得到答案,是比较困难的,暴力的方法是枚举C(n,m)种情况,这样做的时间复杂度是o(n2),在数据量大的情况下我们无法接受。注意到,如果存在一种移除方法,使得相邻两个石头之间距离的最小值不超过M,那么大于M的情况都可以排除掉,因此我们可以在0和L之间二分答案。
检测给定的值是否满足条件的方法是:对于被检测的距离M,不断记录当前石头左边的最后一个石头lastpos,如果二者距离小于M,则移除当前的石头,否则更新lastpos为当前石头。
示例代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
int stones[50005],M,N,L;
bool check(int result){
    int lastpos=0;
    int removed=0;
    for(int i=1;i<=N;++i){
        if(stones[i]-lastpos<result)
            removed++;
        else lastpos=stones[i];
    }
    if(stones[N+1]-lastpos<result)return false;
    return removed<=M;
}

int main(){
    cin>>L>>N>>M;
	stones[0]=0;
	stones[N+1]=L;
	for(int i=1;i<N+1;++i){
		cin>>stones[i];
	}
	sort(stones+1,stones+N+1); 
	int left=0;
	int right=L;
	int mid;
	while(left<=right){
		mid=left+(right-left)/2;
		if(check(mid)){
			left=mid+1;
		}else right=mid-1;
	}
	cout<<right;
}
这道题同样很难正向直接找到最优解,但若给定所需代码量result,解出能做出的答案q很简单。我们可以比较q与k,如果q小于k,则说明result太大了,反之亦然。
需要注意的有两点,一是需要把各种数据的值设为long long,否则会产生溢出的问题,二是当q=k时应该做的处理。如果q==k,说明此时的result是可行的,但不一定是最值,需要更新最小(最大)值,同时继续尝试可行的更小(更大)的方案。
示例代码如下:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int l,k;
int ops[100005];
int check(int result){
    long long now=0;
    int questions=0;
    for(int i=0;i<l;++i){
        now+=ops[i];
        if(now<0)now=0;
        else if(now>=result){
            now=0;
            questions++;
        }
    }
    return questions;
}

int check_min(long long left,long long right){
	int min;
    while(left<=right){
    	long long mid=left+(right-left)/2;
    	long long q=check(mid);
        if(q<k)
			right=mid-1;
		else if(q>k)
			left=mid+1;
		else{
			min=mid;
			right=mid-1;
		}
    }
    return min;
}
int check_max(long long left,long long right){
	int max;
    while(left<=right){
    	long long mid=left+(right-left)/2;
    	long long q=check(mid);
        if(q<k)
			right=mid-1;
		else if(q>k)
			left=mid+1;
		else{
			max=mid;
			left=mid+1;
		}
    }
    return max;
}
int main(){
    cin>>l>>k;
    long long left=0,right=0;
    for(int i=0;i<l;++i){
        cin>>ops[i];
        right+=abs(ops[i]);
    }
    int min=check_min(left,right+1),max=check_max(left,right+1);
    if(min==-1||max==-1)cout<<-1;
    else cout<<min<<" "<<max<<endl;
}

 

posted @ 2026-04-24 21:04  KevinXsll  阅读(41)  评论(0)    收藏  举报