一、二分查找
lower_bound 函数是 C++ 标准库中的一个函数,用于在有序容器中查找第一个大于或等于给定值的元素,二分查找用stl可以很好解决。在寻找数组元素时很方便,且时间复杂度O(logn)。
例如寻找数组中第一个大于等于7的元素的位置,由于lowerbound函数返回的是迭代器所以要减a
int pos=lower_bound(a,a+n,7)-a;//a数组有序
这个stl在数组中很好用,但是如果我想给学生排序,并任意查询一个成绩,找到这个学生,sort和lowerbound都无法使用。
于是想到了模板pair
pair 模板
pair
模板是一个标准模板库 (STL) 中的容器,用于存储两个元素的元组。它提供了对元素的直接访问,并且可以轻松地比较和排序 pair
对象。
template<class T1, class T2>
class pair;
其中:
T1
是第一个元素的类型。T2
是第二个元素的类型。
成员函数
pair
模板提供了以下成员函数:
- 构造函数:
pair()
:创建一个默认构造的pair
对象,其中两个元素都初始化为默认值。pair(const T1& x, const T2& y)
:创建一个pair
对象,其中第一个元素初始化为x
,第二个元素初始化为y
。
- 访问器:
first
:返回第一个元素的引用。second
:返回第二个元素的引用。
- 比较运算符:
<
,>
,<=
,>=
,==
,!=
:比较两个pair
对象的元素。
- 其他运算符:
make_pair(const T1& x, const T2& y)
:创建一个pair
对象,其中第一个元素初始化为x
,第二个元素初始化为y
。
因为pair重载了<,==,所以可以直接比较pair,pair的比较是从前往后比较,先比较第一个如果都相等再比较第二个。
这里的make_pair()是构建一个临时变量,构建完就删除,查询的是first为2的变量。
#include <iostream>
#include <utility>
#include <algorithm>
using namespace std;
//pair按元素逐个比较。
int main(){
pair<int,char> a[100];
int n;cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].first>>a[i].second;
}
sort(a,a+n);
int pos=lower_bound(a,a+n,make_pair(2,'b'))-a;
cout<<a[pos].first<<" "<<a[pos].second<<endl;
return 0;
}
如果需要的类型超过两个,pair不适用了,那么就需要自己构建类型,用结构体举例
#include <iostream>
#include <algorithm>
#define froi for(int i=0;i<n;i++)
using namespace std;
struct student{
int score,num;
bool operator<(const student& other)const{
if(score==other.score) return num<other.num;
else return score<other.score;
}
bool operator==(const student& other)const{
return score==other.score&&num==other.num;
}
}a[1000];
int main(){
int n;cin>>n;
froi{
cin>>a[i].score>>a[i].num;
}
student tmp={90,-1};
sort(a,a+n);//查询第一个大于等于90分的
int pos=lower_bound(a,a+n,tmp)-a;
cout<<a[pos].score<<" "<<a[pos].num;
return 0;
}
只需要在构建的结构体里重载<和==就可以轻松使用sort和lowerbound等比较函数
二、二分答案
二分答案是指答案所在区间满足有序性,那么通过二分的方式来缩小答案所在的区间,如果判断答案所需时间复杂度为n,区间长度为m,那么正常判断所需的时间为O(n*m),而二分查找所需时间O(n*logm)。
从1-100猜数字的问题就是经典二分答案的例子。
这里贴上董晓老师的模板:
Building an Aquarium:题意大概就是给定珊瑚的高度,水池的宽度,问给定水的数量到底能填到多高。每次判断该高度是否满足都需要o(n),所以用二分答案。
#include <iostream>
#define maxn 0x3f3f3f3f
#include <algorithm>
#define int long long
using namespace std;
int a[200000];
bool func(int x,int n,int w){
int sum=0;
for(int i=1;i<=n;i++){
sum+=max(x-a[i],(long long)0);
}
return sum<=w;
}
signed main(){
int t;cin>>t;
while(t--){
int ans=0;
int n,w;cin>>n>>w;
int max=-maxn;
int min=maxn;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]>max) max=a[i];
if(a[i]<min) min=a[i];
}
//判断是否超过.
int sum=0;
for(int i=1;i<=n;i++){
sum+=max-a[i];
}
if(sum<w){
ans=max+(w-sum)/n;
}else{
//上界max,下界min写二分查
int l=min-1,r=max+1;
while(l+1<r){
int mid=(l+r)>>1;
if(func(mid,n,w)) l=mid;
else r=mid;
}
ans=l;
}
cout<<ans<<endl;
}
return 0;
}
Problem - 4004 (hdu.edu.cn) HDU 4004:青蛙跳石子问题,跳的距离区间很大,所以需要二分答案。
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int l,n,m;
int a[510000];
bool judge(int x)//最远一下能跳x,最多限制次数m,看是否能跳到尽头,每个石子位置为m
{
int count=0;
int past=0,now;
for(int i=1;i<=n+1;){
now=i;
if(a[now]-a[past]>x){
count++;
past=now-1;
}else i++;
}
return count<=m;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
while(cin>>l>>n>>m){
for(int i=1;i<=n;i++){
cin>>a[i];
}
a[0]=0;a[n+1]=l;
sort(a,a+n+1);
int maxx=-inf;
for(int i=n+1;i>0;i--){
// a[i]=a[i]-a[i-1];
if(a[i]-a[i-1]>maxx) maxx=a[i]-a[i-1];//最大间隔
}
int l1=maxx-1,r=l+1;
while(l1+1<r){
int mid=l1+r>>1;
if(judge(mid)) r=mid;
else l1=mid;
}
cout<<r<<endl;
}
return 0;
}
二分答案的难点在于写判断函数,即判断方案是否合适。还有一个点就是要找出判断的上下界。