算法与思想——二分查找与二分答案

发布时间 2023-04-10 16:55:39作者: 铃狐sama

算法与思想——二分查找与二分答案

@

一、二分算法-----log2n速度

1.二分前提:有序的数列,,整体成升序或降序,可以中间有相等的数值。

2.二分写法:定义寻找的头和尾,以及中间的量,不断迭代找出最终答案;

代码如下

int Binary_Search(int a[], int n, int key)
{
    int low, high, mid;
    low = 1;     /*定义最底下标为记录首位*/
    high = n;    /*定义最高下标为记录末位*/
    while (low <= high)
    {
        mid = (low + high) / 2;    /*折半*/
        if (key < a[mid])
            high = mid - 1;
        else if (key > a[mid])
            low = mid + 1;
        else
            return mid;
    }
    return -1; // 没找到  
}

3.二分的快速用法

1.upper_bound: upper_bound(begin,end,num) 会从数组的 begin 位置到 end−1 位置二分查找第一个大于 num 的数字。
注意:这个返回值是地址,若是要找到这个a[pos],则,pos为upperbound减去寻找的数组:

#include<bits/stdc++.h>
using namespace std;

int main(){
    int num[6]={1,2,4,7,15,34}; 
    sort(num, num + 6);                           //按从小到大排序 
    int pos = upper_bound(num, num + 6, 7) - num;    //返回数组中第一个大于被查数的值 
    cout << pos << " " << num[pos] << endl;
    return 0;    
}

注意,若是找不到,则会返回end。

二、二分答案

二分答案的思想

二分答案就是对最终答案的值进行二分,具体做法就是每次取一个中间答案,把答案作为已知条件, 判断根据当前答案是否满足题意, 根据判断结果再将区间缩小。**

写法和二分查找基本相同, 只不过要判断的东西比较复杂, 一般写法是通过写一个 check 函数来实现。

二分答案的技巧

二分答案通常是在寻找“合法”与“不合法”的边界,进一步说,是在寻找“刚好合法”的那个位置。
说白了,就是看这个答案会怎么影响已知量,已知量会偏大吗还是会偏小,从而确定这个答案怎么样

二分离不开单调性,我们做这类题目最重要的就是要找到题目中的某个与“合法性”有单调关系的量。(意思同上)

单调关系通常可以表现为: A 越大,越合法, A 越小,越不合法。在题目中总结出这种单调关系,十分有助于进一步思考。
上题目来康康,最简单的:

切木头

有n个木棍,长度不等,现在要将他们切成同等长度的木棍m个,并且每段的长度都为整数。问这m根木棍最长能有多长?

如果分不出来,输出0。

输入格式
第一行2个数:n, m中间用空格分隔(1 <= n <= 100000, 1 <= m <= 10^9) 后面n行:每行1个数,对应木棍的长度(1 <= Li <= 10^9)。
输出格式
输出一个整数,对应木棍的长度。
输入样例
3 10
15
25
12
输出样例
5

#include <bits/stdc++.h>
using namespace std;
int n;
long long a[100005];
long long m;
long long head=0;
long long ed=INT_MIN;
long long find(long long x){
	long long cnt=0;
	for(int i=1;i<=n;i++){
		cnt+=a[i]/x;
	}
	return cnt;
}
int main(){//若ans太大,则m偏小,ans小则m偏大,
//要找m刚刚好的ans最大值 ,这就是单调性
	cin >> n>> m;
	if(n==m&&n==1){
		cout<<"1";
		return 0;
	} 
	for(int i=1;i<=n;i++){
		cin>>a[i];
		ed=max(ed,a[i]);
	}
	if(ed==0){
		cout<<0;
		return 0;
	} 
	if(m==1){
		cout<<ed;
		return 0;
	}
	//end不可能超过最大的a[i],否则一段也切不出来. 
	while(head+1<ed){//加一保证找的全
		long long mid=(head+ed)/2;
		long long mfind=find(mid);
		if(mfind>=m){
			head=mid;//因为找的是最大的满足值,
			//所以偏小的不管是否刚好为m,都应该
			//继续增大可能ans,
		}
		else{
			ed=mid;
		}
	}
	cout<<head;
}

** 最重要的一点**

1.看找的是最大值还是最小值,从而确定二分答案中=和>放在一起还是和<放在一起
2.为了保证搜索充分,head与end要在写二分答案之前找初始值(head=min-1;end=max+1),同时head+1<end才保证能顾到所有结果。