二分小结

本来感觉自己二分应该已经掌握了,但是在一次热身赛中,一道裸的二分题,竟然都没有看出来是二分。。。感觉自己还没有完全掌握什么是二分

二分:通过不断缩小(一半一半)取值的范围,来确定一个最优解。
原理:如果f(i)是真的,那么对于f(j)(j>=i) 恒为真

poj 1064 Cable master
题意:一共有n条绳子,每条绳子的长度为Li,然后我们从这n条绳子中切出k段长度相等的绳子,问长度最大为多少?
我们可以用二分来缩小长度,然后用一个judge函数,来判断是否满足k段,然后最后的结果就是最优解。judge(i):当长度为i的时候能不能分成k段。对了,这道题要小心四舍五入来着。这个题的精度问题真的是。。还是转化成整数来避免他们的精度问题~

poj 2456 Aggressive cows
题意:一共有m头牛,然后有n个牛舍,牛舍都在一条直线上,然后她们的坐标分别是xi,然后问,把m头牛放到n个牛舍中,他们之间的最小距离最大是多少。
感觉这道题,和上面的题好像没有什么区别,思路是将距离的范围缩小,然后看能不能放m头牛,不过这个是求最大的最小值,上面求的直接是最大值。两个的不同就在与judge函数中的等于号放在哪里。

HDU 4282
题意: X^Z + Y^Z + XYZ = K ,给你一个k问你满足这个表达式的x,y,z有几组。
K (0 < K < 2^31) X < Y, Z > 1
所以 z的范围是【2,31)
x的范围在【1,2^15)
y的范围在【2,2^15)
直接暴力枚举,3个for循环肯定超时,
所以枚举x和z,然后二分y就行了。

POJ 3104
这个题刚开始并没有想到是用二分来做,还以为直接用贪心来弄一弄就好。然后发现了不对劲。不好贪。然后看了一下,是二分时间,然后判断这个时间是不是可行的。如果时间i是可行的,那么对于j>i,来说,j一定也是可行的,这个就满足了我们二分的思想。重点是我们的judge函数怎么写,对于时间mid,如果湿度小于或等于mid的,就直接自然烘干。大于的,假设湿度是ai,自然晾干是xi,烘干是yi,然后xi+yi=mid;
yi*k+xi>=ai,所以 yi>=(ai-mid)/(k-1);注意这里要对k=0,进行特判一下。

codeforces 801c Voltage Keepsake
题意:一共有n个用电器,每个用电器开始的时候电量为b,每个单位时间会消耗a。有一个充电器,然后每个单位时间可以充p。问最大时间到其中一个的电量为0,如果可以一直用的话,则输出-1.
刚开始的时候,对其中的过程有点疑惑,这样整体地考虑真的可以么。其实是可以的,我们可以想象这样一个场景,当一个用电器快没电了,我们赶紧将充电器接到用电器那里。当有两个都没有电的话,这个时候就是答案了。

2017陕西邀请赛热身赛第二题
题意,第一行给你两个数n和m,n表示有n个地点,m表示有m个人。
接下来有n个数,表示对应的地点上有几个垃圾(地点从1开始,到n)
m个人从0号位置出发,每个人每秒可以选择向右走一步,还是捡一个垃圾。
问最短需要的时间

这个题开始还是用贪心去做,然后做半天没有做出来。然后是用二分,因为这里求的是最短的时间。符合单调性。
然后我们的judge函数怎么写,从后往前遍历一遍,然后人数和m比较一下

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1e4+5;
const long long inf=1e14+5;
long long a[maxn],b[maxn];
int n,m;
bool judge (long long x)
{
    for(int i=1;i<=n;i++)
        a[i]=b[i];
    int num=1;
    long long rest=x-n;
    if(rest<=0) return 0;
    for(int i=n;i>=1;)
    {
        if(a[i]==0)
        {
            i--;
            continue;
        }
        if(a[i]<=rest)
        {
            rest-=a[i];
            i--;
        }
        else {
            a[i]-=rest;
            num++;
            rest=x-i;
        }
    }
    if(num>m) return 0;
    else return 1;
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&b[i]);
    long long l=0,r=inf,res=0;
    while(l<=r)
    {
        long long mid=l+(r-l)/2;
        if(judge(mid)){
            res=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    cout<return 0;
}

你可能感兴趣的:(分治)