二分(二分答案)问题合集

二分问题有很明显的关键字如:最大值最小,最小值最大
或者在数据上发现只有log级别才能接受时,我们都应该优先考虑二分。
二分问题的难点也不在二分,而是如何使用贪心配合二分,贪心策略往往是难点所在。

C. Schedule Management

题意:n个工人,m个任务,每个任务都指定哪个工人适合这个任务,如果由适合的工人来做这个任务就只花费1小时,否则要花费2小时。求最少花费多少小时

关键字:花费时间最小。

思路:贪心思路是,如果对于我们规定时间,这个工人完成不所有指定任务,那么就把所有没完成的任务累加起来,可以完成任务的工人要利用起来去帮助完成这些多出的任务。

值得注意的是:我们需要先统计出所有工人不能完成的任务数,然后再去找人帮忙,不能凭空帮忙。

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int N = 2e5+100;
int a[N],n,m;
bool check(int num)
{
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]>num)
        {
            sum+=a[i]-num;
        }
        else
        {
            sum-=(num-a[i])/2;
            sum=max(sum,0);
        }
    }
    return sum==0?true:false;
}
bool cmp(int a,int b)
{
    return a>b;
}
int main()
{
    int t;
    for(cin>>t;t;t--)
    {
        cin>>n>>m;
        for(int i=1,temp;i<=m;i++)
        {
            cin>>temp;
            a[temp]++;
        }
        sort(a+1,a+n+1,cmp);
        int l=0,r=2*m;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(check(mid))
            {
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        cout<<l<<endl;
        for(int i=1;i<=n;i++)
        {
            a[i]=0;
        }
    }
    return 0;
}

精度相关的二分问题
P1577 切绳子

#include <bits/stdc++.h>
using namespace std;
double a[10000+10],l,r=1000000,mid;
int n,k;
bool check(double mid){
    int ans=0;
    for(int i=1;i<=n;i++)
       ans+=a[i]/mid;
    return ans>=k;
}
int main(){
    //freopen("in.txt","r",stdin);
    cin>>n>>k;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    while(r-l>=0.001){
        mid=(l+r)/2.0;
        if(check(mid))
            l=mid+0.001;
        else
            r=mid-0.001;
    }
    cout<<r<<endl;
    return 0;
}

C. Balanced Stone Heaps

题意:给出n堆石头,然后从第三堆开始从前往后,每堆都可以拿出3x然后给其前放第一堆x个石头,前放第二堆2x个石头。问,堆石头最小个数最大是多少。

最小个数最大,一眼二分。

第一个二分:直接莽上去,只要有大于我给定num值的统统分配。
但是这样发现一个问题,之所以是大于num值才分配,是因为我们不希望产生新的最小值,但是如果这个位置的石头在接受这个位置后方的石头给予后,这堆石头仍然有可能会产生冗余的石头。

我们不妨从后往前进行石头的分配,最后保证了,除了第一第二项外其他所有堆石头必然是符合最小值条件的,然后再检查前两项即可。

#include <iostream>

#define endl '\n'
#define int long long
using namespace std;
const int N = 2e5+100;
int h[N],n;
int a[N];
int b[N];
bool check(int num)
{
    for(int i=1;i<=n;i++)
    {
        h[i]=a[i];
        b[i]=0;
    }
    for(int i=n;i>=3;i--)
    {
        if(h[i]+b[i]>=num)
        {
            int x=(b[i]+h[i]-num)/3;
            x=min(x,h[i]/3);
            b[i-1]+=x;
            b[i-2]+=x*2;
        }
        else
            return 0;
    }
    if(b[1]+h[1]<num)
    {
        return 0;
    }
    if(b[2]+h[2]<num)
        return 0;
    return 1;
}
signed main()
{
    int t;
    for(cin>>t;t;t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        int l=0,r=1e9+1;
        while(l<=r)
        {
            int mid=((l+r)/2);
            if(check(mid))
            {
                l=mid+1;
            }
            else
            {
                r=mid-1;
            }
        }
        cout<<r<<endl;
    }
    return 0;
}

TO BE CONTINUE。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值