二分算法系列(二分查找与二分答案)

知识点讲解

伪代码如下: 视频链接:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结论:
在这里插入图片描述

如何设置蓝红边界:
在这里插入图片描述

一般流程:
在这里插入图片描述


二分查找

查找

查找
在这里插入图片描述

#include<iostream>
#include<vector>

using namespace std;

int main()
{
	int n,m;
	cin >> n >>m;
	vector<int > vec(n);
	for(int i=0;i<n;i++){
		cin >> vec[i];
	} 
	while(m--){
		int x;
		cin >>x;
		int l = -1, r = n;
		while(r>l+1){
			int mid = (r+l)>>1;
			if(vec[mid]>=x){
				r = mid;
			}else{
				l = mid;
			}
		}
		if(vec[r]==x)cout << r+1 << " ";// 题目中的数组下标从1开始
		else cout << -1 <<" ";
	}
	
	return 0;
}


二分答案

木材加工

木材加工在这里插入图片描述

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

long long int check(long long int mid, vector<long long int> vec, long long int n) {
    long long int sum = 0;
    for(int i=0;i<n;i++){
        sum+=vec[i]/mid;
    }
    return sum;
}

int main() {
    long long int n, k;
    cin >> n >> k;
    vector<long long int> vec(n);
    for (int i = 0; i < n; i++) {
        cin >> vec[i];
    }

    long long int l = 0, r = 100000000;  // 表示木条长度的范围
    while (l + 1 < r) {
        long long int mid = (l + r) >> 1;
        if (check(mid, vec, n) >= k) {//k为木条数量,check返回值>=k,说明切的太多段了,这样每段偏小
        							  //-->应该让每段长一些
        							  //-->左指针右移(l = mid)
            l = mid;
        } else {
            r = mid;
        }
    }
    if (l < 1)
        cout << 0;
    else
        cout << l;
        
    return 0;
}
  • 一开始写的是:
while(l+1<r){
		int mid=(l+r)/2;
		if(ans_(mid,vec,n)>k){ //这里是>l=mid;
		}else{
			r=mid;
		}
	}
	
	if(r<1)cout << 0;  
	else cout << r;  //返回的是r
  • 但发现结果是错的,所以改成 >=k返回 L
  • 哦,AC了,那就这样改喽,不管他啦,嘿嘿嘿
  • 还有还有:long long yyds

砍树

再看一题:砍树

  • 异曲同工。。。。

进击的奶牛

进击的奶牛在这里插入图片描述

#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#define lld long long int
using namespace std;

lld check(lld mid, vector<lld>vec, lld n){
    lld pre = vec[0]; // 先设置第一个数字,接下来需要找>=mid长度的区间,pre为区间的左坐标
    lld sum = 0; // 记录满足的区间个数,最后牛牛的个数比区间个数多1
    for(lld i=0;i<n;i++){ // 从区间的左坐标pre对应的数组下标0开始向右找区间
        if(vec[i]-pre >= mid){ // 该区间满足
            sum++;
            pre = vec[i];
        }
    }    

    return sum+1;
}

int main(){
    lld n,c;
    cin >> n >> c;
    vector<lld> vec(n);
    for(lld i=0;i<n;i++){
        cin >> vec[i];
    }

    sort(vec.begin(),vec.end());

    lld l = -1,r = vec[n-1]-vec[0]+1; // 相邻两头牛的距离区间
    while(l+1<r){
        lld mid = (l+r) >> 1;
        if(check(mid,vec,n) >= c){ // 如果check出来的牛比要求的多,说明牛牛相隔太近了,所以mid应该大一些,即l右移
            l = mid;
        }else{
            r = mid;
        }
    }

    cout << l;

    return 0;
}

跳石头

好题:跳石头
好题解:ShawnZhou、Michael_Li
在这里插入图片描述

#include <iostream>
#include <vector>
#define lld long long int

using namespace std;

lld check(lld mid, vector<lld> vec, lld n) {
    lld sum = 0, pre = 0; // pre表示选手所站的位置(石头)
    for (lld i = 0; i <= n; i++) {
        if (vec[i] - pre < mid) {// 如果所站的石头距离下一个石头的位置比mid小,那就把下一个石头移走
            sum++;
        } else {// 否则跳到下一个石头上
            pre = vec[i];
        }
    }

    return sum;
}

int main() {
    lld ll, n, m;
    cin >> ll >> n >> m;
    vector<lld> vec(n + 1);
    for (lld i = 0; i < n; i++) {
        cin >> vec[i];
    }
    vec[n] = ll;  // ll表示起点到终点的距离,那就把最后一个点也加进去

    lld l = -1, r = ll + 1;  // 跳跃距离
    while (l + 1 < r) {
        lld mid = (l + r) >> 1;
        if (check(mid, vec, n) > m) { // m为要移走的石头个数,如果check出来的个数偏多,说明剩下的石头偏少,距离偏大
            r = mid;// 所以应该少移一些石头,让mid减小
        } else {
            l = mid;
        }
    }

    cout << l;

    return 0;
}

  • 一开始写的是:
    lld l = 0; // 得按模板来l=-1    //木材加工里因为有限制最小为1,所以写l=0
	lld r = ll;  //得按模板来r=ll+1   
	while( l+1 < r){
		lld mid= (l + r) >> 1; 
		if(check(mid, vec, n) >= m){   // 这里是>=
			r = mid;	  
		}else{
			l = mid;
		}
	}
	
	cout << r;  // 这里是r
  • 然后,改改就ok啦

卡牌

卡牌
在这里插入图片描述

#include <iostream>
#include <vector>
#define lld long long
using namespace std;

lld check(lld mid, vector<lld> kapai, vector<lld> kongpai, lld n) {
    lld sum = 0;  // 空牌数
    for (lld i = 0; i < n; i++) {
        if (kapai[i] + kongpai[i] >= mid) {
            sum += (mid - kapai[i]);  // 表示要补的卡牌数
        } else {
            return -1;  // 第i种牌都不够mid的数量,说明mid套牌肯定凑不齐
        }
    }

    return sum;
}

int main() {
    lld n, m;
    cin >> n >> m;
    vector<lld> kapai(n);
    vector<lld> kongpai(n);
    lld max = 0;
    for (lld i = 0; i < n; i++) {
        cin >> kapai[i];
        if (kapai[i] > max)
            max = kapai[i];
    }

    for (lld i = 0; i < n; i++) {
        cin >> kongpai[i];
    }

    lld l = -1, r = max + 1;// 能得到的套牌的套数
    while (l + 1 < r) {
        lld mid = (l + r) >> 1;
        lld return_kongpai = check(mid, kapai, kongpai, n);
        if (return_kongpai >= m || return_kongpai == -1) { // m为提供空牌数
                                                           // 如果check的结果比给的空牌多,说明能组成的套牌过多了
                                                           // 所以套牌应该减少,即mid减少
            r = mid;// 让套牌的右区间往左移动
        } else {
            l = mid;
        }
    }

    cout << l;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值