Living-Dream 系列笔记 第4期

本期主要讲解二分答案。

知识点

使用场景:

  • 最小值最大化,或最大值最小化。

  • 在限制条件下找最值。

与二分查找的区别:

  • LR 均为答案,而非下标。

输出:

  • 最大化输出 L,反之输出 R

例题

T1

二分 \(M\) 的值,边界为 \(L=-1,R=\max{\{a_i\}}\)。每次枚举到一个 \(mid\) 就对于每个 \(a_i\),计算能砍下的木头是否 \(\ge m\)

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

int n,m,maxh,h[1000031];

bool check(int x){
    int sum=0;
    for(int i=1;i<=n;i++)
        if(h[i]>x) sum+=h[i]-x;
    return sum>=m;
}

signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>h[i],maxh=max(maxh,h[i]);
    int l=-1,r=maxh+1;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(check(mid)) l=mid;
        else r=mid;
    }
    cout<<l;
    return 0;
}

T2

二分 \(l\) 的值,边界为 \(L=0,R=\max{\{a_i\}}\)(注意 \(L\)\(0\),这样可以免去不必要的特判)。

每次枚举到一个 \(mid\),则当前段数为 \(\sum^{n}_{i=1} \lfloor \dfrac{a_i}{mid} \rfloor\),判断其是否 \(\ge m\) 即可。

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

int n,k,l[100031];

bool check(int x){
	int sum=0;
	for(int i=1;i<=n;i++) sum+=l[i]/x;
	return sum>=k;
}

signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>l[i];
	
	int l=0,r=1e8+1;
	while(l+1<r){
		int mid=(l+r)>>1;
		if(check(mid)) l=mid;
		else r=mid;
	}
	cout<<l;
	return 0;
}

习题

T3

首先读入字符串,分离并计算出做一个汉堡需要的 \(B,S,C\)。记读入的已有材料数分别为 \(nb,ns,nc\),需要的钱数分别为 \(mb,ms,mc\),已有钱数为 \(money\)

二分能做出的汉堡个数,边界为 \(L=-1,R=money+\max{(nb,ns,nc)}\)。对于每个 \(mid\),计算出除去已有材料后还需要花的钱,与 \(money\) 作比较即可。

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

string str;
int b,s,c;
int nb,ns,nc;
int mb,ms,mc;
int money;

bool check(int x){
	int sum=0;
	if(x*b>nb) sum+=(x*b-nb)*mb;
	if(x*s>ns) sum+=(x*s-ns)*ms;
	if(x*c>nc) sum+=(x*c-nc)*mc;
	return sum<=money;
}

signed main(){
	cin>>str;
	cin>>nb>>ns>>nc;
	cin>>mb>>ms>>mc;
	cin>>money;
	
	for(int i=0;str[i];i++){
		if(str[i]=='B') b++;
		else if(str[i]=='S') s++;
		else c++;
	}
	
	int l=-1,r=money+max(nb,max(ns,nc))+1;
	while(l+1<r){
		int mid=(l+r)>>1;
		if(check(mid)) l=mid;
		else r=mid; 
	}
	cout<<l;
	return 0;
} 

T4

记需要 \(x\) 轮才能满足所有人的要求,且所有人共需要玩 \(s\) 轮游戏。

由定义可知 \(s=\sum^{n}_{i=1} a_i\),并且由题意有 \((n-1)x \ge s\)

则可以二分 \(x\) 的值,边界为 \(L=\max{\{a_i\}}-1,R=s+1\)

每次枚举到一个 \(mid\),就按照上式检查即可。

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

int n,s,m,a[100031];

signed main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],s+=a[i],m=max(m,a[i]);
    int l=m-1,r=s+1;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(mid*(n-1)>=s) r=mid;
        else l=mid;
    }
    cout<<r;
    return 0;
}
posted @ 2024-03-09 12:31  _XOFqwq  阅读(1)  评论(0编辑  收藏  举报