Cable master POJ - 1064
【浮点数二分】
一般题目会要求输出相应的精度,而这类题目就容易错在这里。可以通过人为的控制循环来达到所要求的精度,或者设立相应的终止条件。

#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e4+5;
double pie[N];
int n,k;
int main()
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        double left=0,right=inf;//区间足够大
        for(int i=1;i<=n;i++)
            scanf("%lf",&pie[i]);
        for(int i=1;i<=100;i++)
        {
            double mid=(left+right)/2;
            int num=0;
            for(int j=1;j<=n;j++)
                num+=(int)(pie[j]/mid);
            if(num>=k)
                left=mid;
            else
                right=mid;
        }
        printf("%.2f\n",floor(right*100)/100);//先乘100,然后向下取整,可以直
       // 接把百分位后面的位数舍弃,再除以100,即可保留2位小数。
    }
    return 0;
}

Aggressive cows POJ - 2456
【最大值最小化问题】
大概思路,二分枚举可能距离,然后验证是否满足。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e5+5;
const int maxn=1e9+5;
int x[N];
int n,c;
bool check(int dis)
{
    int last=1,cnt=last+1;//从第一个地方开始安排
    for(int i=2;i<=c;i++)
    {
        while(cnt<=n&&x[cnt]-x[last]<dis)
            cnt++;
        last=cnt;
        if(cnt==n+1)//如果能找到,那么cnt必定<=n
            return false;
    }
    return true;
}
int main()
{
    while(scanf("%d%d",&n,&c)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&x[i]);
        sort(x+1,x+1+n);
        int left=0,right=maxn;
        while(left<=right)
        {
            int mid=(left+right)>>1;
            if(check(mid))
                left=mid+1;
            else
                right=mid-1;
        }
        printf("%d\n",right);
    }
    return 0;
}

hdu4430
题目要求满足条件的两个数,这时就要选择对哪个数进行二分。
解题策略是:根据n的取值和等比数列求和公式,可以大概估计出r的取值范围,为1~40。同时,有了r的取值,就可以通过通过开方的途径大概求出k的上界,然后在对k进行二分。即对r进行枚举,每次都对k进行一次二分。
这样就能保证在确定一个量的前提下,对另一个量进行二分,保证二分是在单调的情况下进行的。
当有多个量时,应该考虑固定一个看另外几个。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,r,k;
ll solve(ll x,ll y)
{
    ll res=0,num=1;
    for(int i=1;i<=y;i++)
    {
        num*=x;
        res+=num;
    }
    if(res==n||res==n-1)//求最佳的r和k值
    {
        if(x*y<r*k)
        {
            k=x;
            r=y;
        }
        else if(x*y==r*k&&r>y)
        {
            k=x;
            r=y;
        }
    }
    return res;
}
int main()
{
    while(scanf("%lld",&n)!=EOF)
    {
        k=n-1,r=1;//k=n-1会错误,因为中间可以放1个,为了使k*r最小,当n=18,就会发现错误
        for(int i=2;i<=40;i++)//根据n的最大值和等比数列求和大概可以估计r的取值范围
        {
            ll left=2,right=pow(n,1.0/i);//当r取不同值时,k对应的范围
            while(left<=right)//对k进行二分
            {
                ll mid=(left+right)>>1;
                ll tm=solve(mid,i);
                if(tm>n)
                    right=mid-1;
                else
                    left=mid+1;
            }
        }
        printf("%lld %lld\n",r,k);
    }
    return 0;
}