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;
}

浙公网安备 33010602011771号