自定义类型二分查找与二分答案

一、二分查找

        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猜数字的问题就是经典二分答案的例子。

        这里贴上董晓老师的模板:

        

 Problem - E - Codeforces

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

二分答案的难点在于写判断函数,即判断方案是否合适。还有一个点就是要找出判断的上下界。

  • 16
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值