poj 1064 Cable master

二分

题意:给出n条线段,以米的单位给出,小数点后两位(精确到厘米),要你对这些线段裁剪,裁剪出m条等长的线段,并且让这些线段尽可能长另外线段的长度不能小于1厘米,如果筹不够m条,输出0.00

做法就是二分答案,但是二分的区间有讲究

一开始我认为长度的上限应该是  min { len[i] },即最短的那条线段,后来才想到是错的,应该是max{ len[i] },因为有一点想当然了,认为裁剪线段,就是每条线段都要裁剪,其实不是的,并不是每条线段都要裁剪

好像

4 2

2.00

3.00

4.00

答案是2.00吗?不是的,应该是3.00,虽然3.00大于第一条线段的长度,那么就不要裁剪第1条线段,直接从第2,第3条线段开始裁剪,就能裁剪出2条3.00长度的线段

 

另外,这题可以用浮点数的二分来写,但是基于题目的意思,完全可以转化为整数

因为最后长度不能小于1厘米,而一开始给出的线段也是精确到厘米的,而答案也是要求精确到厘米的,那么为什么我们不一开始就把所有的数据都改成用厘米来表示呢?然后直接用整数来二分就可以避免掉所有精度的问题。所以一开始的二分区间就是[1,max{len[i]}] , 不要从0开始,完全没有比较,因为答案最小要为1

最后一点,也是wa的地方,想了一阵子想明白了

我们二分答案,得到一个长度l,然后用所有线段去除l,看能裁剪出多少条,然后统计条数的总和count

然后就分类

count < m,说明这个长度l太长,筹不够m条,所以要缩点长度l,去左半区间二分

count = m,说明是一个合法的答案,记录,但是这样就可以了吗?可以跳出了吗?不是的,因为要找最大值,所以不能跳出,继续到右半区间二分

count > m , 这是不是一个合法的答案?一开始没想清楚,认为不是合法的答案,其实是的!另外要到右半区间继续二分找一个更大的值

 

下面的这个二分是错误的,就是因为count和m的判断出错

首先count>m的情况是一个合法的答案,最要命的是,可能找不到满足条件的count == m,这能找到count > m

例如

3 5

3.00

3.00

3.00

答案是1.00,当答案是1.00的时候count=9 > 5 , 当答案为2,3的时候,count都无法等于m,根本无法更新答案

因为想歪了一点,虽然题目要筹够m条,如果无法准确筹够m条的时间,只能缩短长度,筹出多余m条线段,因为裁剪多出来的线段,并不规定一定要用完

while(low <= high)

        {

            int mid = (low + high) >> 1;

            int count = 0;

            for(int i=0; i<n; i++)

            {

                int k = a[i] / mid;

                count += k;

            }

            if(count == m)

            {

                res = max(res , mid);

                low = mid + 1;

            }

            else if(count > m)

                low = mid + 1;

            else

                high = mid - 1;

        }

 

 

最后,用scanf读入数据,cin在G++会超时,在C++上没问题

 

用整数来二分

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cmath>

using namespace std;

#define N 10010

#define MAX 10000000



int a[N];



int main()

{

    int n,m;

    double len;

    while(scanf("%d%d",&n,&m)!=EOF)

    {

        int Max = 0;

        for(int i=0; i<n; i++)

        {

            scanf("%lf",&len);

            a[i] = len * 100;

            Max = max(Max , a[i]);

        }

        int low = 1 , high = Max;

        int res = 0;

        while(low <= high)

        {

            int mid = (low + high) >> 1;

            int count = 0;

            for(int i=0; i<n; i++)

                count += a[i] / mid;



            if(count >= m)

                res = max(res , mid) , low = mid + 1;

            else

                high = mid - 1;

        }

        printf("%.2f\n",(double)res / 100.0);

    }

    return 0;

}

 

 

用浮点数二分,不推荐(没改scanf,交G++会超时)

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cmath>

#include <algorithm>

using namespace std;

const int N  = 10010;

const double E = 0.0001;

const double M = 0.01;



double a[N];



int main()

{

    int n,m;

    double Max;

    while(cin >> n >> m)

    {

        Max = 0.0;

        for(int i=0; i<n; i++)

            cin >> a[i] , Max = max(Max,a[i]);

        double low = 0 , high = Max;

        while(high - low > E)

        {

            double mid = (low + high) / 2.0;

            int count = 0;

            for(int i=0; i<n; i++)

            {

                double s = a[i] / mid;

                int k = (int)s;

                count += k;

            }

            if(count >= m)

                low = mid;

            else

                high = mid;

        }

        int tmp = high * 100;

        double res = (double)tmp * 0.01;

        printf("%.2f\n",res);

    }

    return 0;

}

 

你可能感兴趣的:(master)