二分搜索专题

2 篇文章 0 订阅

假定一个解并判断是否可行 POJ1064

C(x) = Ai/x的总和是否大于或等于k

#include<iostream>
#include<cstdio>
#include<math.h>
#include<string>
using namespace std;
int N,K;
double a[10005];
bool C(double x)
{
    int num = 0;
    for(int i = 0;i < N;i++)
    {
        num += (int)(a[i]/x);
    }
    return num >= K;
}
int main()
{
    cin >> N >> K;
    for(int i = 0;i < N;i++)
    {
        cin >> a[i];
    }
    double left = 0,right = 2000000; //二分搜索上限至少是最大值的两倍 经验!
    for(int i = 0;i < 100;i++)
    {
        double mid = (left+right)/2;
        if(C(mid)) left = mid;
        else right = mid;
    }
    printf("%.2f\n",floor(left*100)/100);最小两位取整的做法
}

每次判断的时间复杂度为O(N)

最大化最小值

POJ2456

C(d)=可以安排牛的位置使得任意的牛的间距都不小于d

1.对房子的位置x进行排序

2.把第一头牛放入第一个房子

3.如果第i头牛放入Xj的话,第i+1头牛就要放入Xj+d <=Xk的最小k中

#include<iostream>
#include<cstdio>
#include<math.h>
#include<string>
#include<algorithm>
using namespace std;
int N,M;
int a[10005];
bool C(int x)
{
    int last = 0;
    for(int i = 1;i < M;i++)
    {
        int cnt = last + 1;
        while(cnt < N && a[cnt] - a[last] < x)
        {
            cnt++;
        }
        if(cnt == N)
            return false;
        last = cnt;
    }
    return true;
}
int main()
{
    cin >> N >> M;
    for(int i = 0;i < N;i++)
    {
        cin >> a[i];
    }
    sort(a,a+N);
    int left = 0,right = 2000000;
    for(int i = 0;i < 100;i++)
    {
        int mid = (left+right)/2;
        if(C(mid)) left = mid;
        else right = mid;
    }
    printf("%d\n",left);

}

最大化平均值

有n个物品的重量和价值分别是wi和vi。从中选出k个物品使得单位重量的价值最大

用贪心将性价比最高的选出是不可行的,只能用二分。

sum v / sum w >= x  推出公式v - x*w  >= 0

因此可以对公式进行排序贪心选取了

#include<iostream>
#include<cstdio>
#include<math.h>
#include<string>
#include<algorithm>
using namespace std;
int n,k;

int y[10005];
int w[10005];
int v[10005];
bool cmp(int a,int b)
{
    return a > b;
}
bool c(int mid)
{
    double sum = 0;
    for(int i = 0;i < n;i++)
    {
        y[i] = v[i] - mid * w[i];//公式为Vi-xWi >= 0
    }
    sort(y,y+n,cmp);
    for(int i = 0;i < k;i++)
    {
        sum += y[i];
    }
    return sum >= 0;
}
int main()
{
    cin >> n >> k;

    for(int i = 0;i < n;i++)
    {
        cin >> w[i] >> v[i];
    }
    double left = 0;
    double right = 1000000;
    for(int i = 0; i < 100;i++)
    {
        double mid = (left+right)/2;
        if(c(mid))
        {
            left = mid;
        }
        else
        {
            right = mid;
        }
    }
    printf("%.2f\n",left);
}

折半枚举(双向搜索)

POJ NO.2785

#include<iostream>
#include<cstdio>
#include<math.h>
#include<string>
#include<algorithm>
using namespace std;
int N;
int A[4005];
int B[4005];
int C[4005];
int D[4005];
int AB[4005*4005];
int main()
{
    cin >> N;
     for (int i=0;i<N;i++) cin>>A[i]>>B[i]>>C[i]>>D[i];
    for(int i = 0;i < N;i++)
        for(int j = 0;j < N;j++)
            AB[i*N + j] = A[i] + B[j];
    sort(AB,AB+N*N);
    long res =  0;
    for(int i = 0;i < N;i++)
        for(int j = 0;j < N;j++)
        {
            int cd = -(C[i] + D[j]);
            res += upper_bound(AB,AB+N*N,cd) - lower_bound(AB,AB+N*N,cd);
        }
    cout << res << endl;
}


POJ3104

第一次见需要推公式的二分,还是做题少啊。

先列出公式,X为用吹风机的时间,Y为自然风干的时间,我们这里需要找出mid使得mid = x+y;

y+x*k >= Ai 两个式子相减就是k-1 !不是k!

#include<iostream>
#include<stdio.h>
#include<cmath>
#include<string.h>
#include<algorithm>
typedef long long ll;
using namespace std;
ll n, k;
ll a[1000005];
bool C(ll x)
{
    ll crt=0;
    for (int i = 0; i <n; i++)
    {
        if (a[i] > x)
        {
            crt += (a[i] - x + (k - 2)) / (k - 1);
            if (crt > x) return true;
        }
    }
    return false;
}
int main()
{
    while (scanf("%lld", &n) != EOF)
    {
        for (int i = 0; i < n; i++)
            scanf("%lld", &a[i]);
        scanf("%lld", &k);
        sort(a, a + n);
        if (k == 0 || k == 1)
            printf("%d\n", a[n - 1]);
        else {
            ll l = 0, h = 0x3f3f3f3f;
            while (h - l > 1)
            {
                ll mid = (l + h) / 2;
                if (C(mid)) l = mid;
                else h = mid;
            }
            printf("%lld\n", h);
        }
    }
    return 0;
}

求a,b之间有多少个斐波那契额数,二分思想nb呀
import java.math.BigInteger;    
import java.util.*;   
  
public class Main {  
    public static void main(String[] args) {  
        Scanner cin=new Scanner(System.in);  
        BigInteger a[]=new BigInteger[10500];  
        a[1]=BigInteger.valueOf(1);  
        a[2]=BigInteger.valueOf(2);  
        for(int i=3;i<10500; i++)  
        a[i]=a[i-1].add(a[i-2]);  
        BigInteger n,b;  
        while(cin.hasNextBigInteger()){  
            n=cin.nextBigInteger();  
            b=cin.nextBigInteger();  
            if(n.equals(BigInteger.ZERO) && b.equals(BigInteger.ZERO))    
            break;   
            int sum1=0,sum2=0;  
            int low,high,mid;  
            low=1;high=10500;  
            while(low<=high){  
                mid=(low+high)/2;  
                if(a[mid].compareTo(n)==0){  
                    sum1=mid-1;  
                    break;  
                }  
                else if(a[mid].compareTo(n)<0)  
                low=mid+1;  
                else high=mid-1;  
            }  
            if(sum1==0&&!n.equals(BigInteger.valueOf(1)))sum1=high;  
            low=1;high=10500;  
            while(low<=high){  
                mid=(low+high)/2;  
                if(a[mid].compareTo(b)==0){  
                    sum2=mid;  
                    break;  
                }         
                else if(a[mid].compareTo(b)<0)  
                low=mid+1;  
                else high=mid-1;  
            }  
            if(sum2==0&&!b.equals(BigInteger.valueOf(1)))sum2=high;  
            System.out.println(sum2-sum1);  
        }  
    }  
  
}  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值