前言:二分法一般是对有序数组进行操作,但也可以解决无序数组查找峰值的问题
例:有一个n位数的无序数组,如[5,3,4,2,7,6,9],认为两侧的位置都是无穷小,返回其中一个峰值的下标,0,2,4,6都可
解法:先检查0位置和n-1位置看是不是峰值,若都不是,中间一定会有峰值(左侧上升,右侧下降),看mid是否是峰值,若不是,若mid是下坡,则往左找,若mid是上坡,往右找(找一个就行了)
部分函数代码:
int f(int n){ //a存储数组元素
if(n==1) return 0; //0位置是峰值
if(a[0]>a[1]) return 0;
if(a[n-1]>a[n-2]) return n-1; //n-1是峰值
int l=1,r=n-2,ans=-1;
while(l<=r){
int mid=(l+r)/2;
if(a[mid-1]>a[mid]){
r=mid-1;
}else if(a[mid]<a[mid+1]){
l=m+1;
}else{
ans=mid;
break;
}
}
return ans;
}
三分法是在二分法的基础上提出的,增加了可求取极值的函数的种类(用来确定函数在凹/凸区间上的极值点),下图取自oceanstar的笔记三分法查找详解-优快云博客,由图看出,不论m1,m2如何分布,若m1>m2,则极值点在m1左侧,l=m1+1
若用三分法解决例题(求峰值下标),部分函数代码如下:
int f(int n){
int l=0,r=n-1;
while(l<r){
int midl=l+(r-l)/3,midr=r-(r-l)/3;
if(a[midl]<a[midr]){ //峰值在midl右边
l=midl+1;
}else{
r=midr-1;
}
}
return l; //或者r,二者相等
}
01分数规划也是在二分法的基础上,用来求分数的最值
题目:小咪买东西 NC14662
思路:题目等价于求一组w[],w[i]为0或1,使得
那么应该选vi-x*ci最大的k件物品
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int n,k;
int c[10010],v[10010];
long long q[10010];
bool check(int x){
long long s=0;
for(int i=0;i<n;i++){
q[i]=v[i]-c[i]*x;
}
sort(q,q+n);
for(int i=n-k;i<n;i++){ //最后k件和
s+=q[i];
}
return s>=0;
}
int main(){
int T,i;
cin>>T;
while(T--){
cin>>n>>k;
for(i=0;i<n;i++){
cin>>c[i]>>v[i];
}
int l=0,r=1e4,max1=-1; //max1的范围在(0,10^4)
while(l<=r){
int mid=(l+r)/2;
if(check(mid)){
max1=(int)mid;
l=mid+1;
}else{
r=mid-1;
}
}
cout<<max1<<endl;
}
return 0;
}
END