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;
}

 

posted @ 2013-06-11 18:37  Titanium  阅读(2750)  评论(0编辑  收藏  举报