切绳子、如何放牛(二分)

描述

有n条绳子,长度分别为L[i]。如果从他们中切割出k条长度相同的绳子的话,这k条绳子每条最长能有多长?(答案保留小数点后两位,规定1单位长度的绳子最多可以切割成100份)

输入格式

输入n,k,(1<=n,k<=10000)
然后n行,输入L[i],代表每一条绳子的长度(1<=L[i]<=100000)

输出格式

切出k条长度相等的绳子最大长度是多少,输出保留两位小数

样例输入

4 11
8.02
7.43
4.57
5.39

输出

2.00

题意

n条绳子能切出k条一样的绳子,最长多长

思路

固定总长和段数要最长的相同长度,没错,就是再0到最长绳子之间二分,其实一开始我想的是0到最短的绳子,我想当然的认为既然所有绳子都要切,也就是最短的也得切最终答案会小于最短的,其实不切也行,所以应该是0到最长的绳子二分。二分后将mid值进行判断,每根绳的长度初以这个mid,就是看能切除几根,加起来与k值进行比较,如果大了,证明还能再切长一点,也有可能这个和刚好就等于k,但是我们不能返回,我们要贪心一点,得试试要是距离再开大一点点,是不是还是得到这个数,于是依然left=mid+1,往大的半段搜索。当我们一路搜索到right<mid时,搜索就要结束了,返回一个值。这个值是right还是mid还是left呢?就要看最后我们得到符合要求的距离后的“再开大一点点”是往哪边开了,如果我们往右开,也就是left=mid+1,那么left就变了,我们得返回right,如果我们往左开,我们改动了right,说明得返回left。

这个题最恶心的就是这个精度啊,我在这wa了八次,处理方法就是我们在输入的时候先乘100再存入数组,最后出答案时再除以100.0转成double,记得小数点后保留两位有效数字。

AC代码

#include<iostream>
#include<cstdio>
using namespace std;
int n,k;
double e,a[100010];
int  judge(int m)
{
    int sum=0;//用于记录绳子数
    for(int i=1;i<=n;i++)
    {
        sum+=a[i]/m;
    }
    if(sum>=k)//等于时还得再来
        return 1;
    else 
        return 0;
}
int main()
{
    while(cin>>n>>k)
    {
     int maxs=0;
     for(int i=1;i<=n;i++)
     {
        scanf("%lf",&e);
        a[i]=e*100;//扩大后存入
        if(maxs<a[i])//得到最长绳子长度
        maxs=a[i];
     }
     int left=0,mid,right=maxs;
     while(left<=right)
     {
        mid=(left+right)/2;
        if(judge(mid))
            left=mid+1;
        else
            right=mid-1;
     }
     double res=right/100.0;//是100.0
     printf("%.2lf\n",res);
 }
    
    return 0;
}

描述

农夫有n个牛栏,m头牛,然后要让你把m个牛都放进牛栏里,让两头牛之前的最大的最小距离

输入格式

输入n,m (1<=m<=n<=100000)
下面n行是牛栏的位置xi (0 <= xi <= 1,000,000,000)

输出格式

输出两头牛最大的最小距离

样例输入

5 3
1
2
8
4
9

样例输出

3

题意

求出给定的n个直线排列的牛栏中,每只牛被放入后两牛之间最小距离的最大值是多少。

思路

一开始看到这个最小距离的最大值是一脸懵逼的,根本没有思路,一直在想要对什么二分,此处我们要查找的量是距离,那么应该用可以取到的距离的最大值和最小值进行二分。最大距离就是整个牛栏的长度,也就是第一个牛栏到最后一个牛栏的距离,最小距离懒得算了,就取0吧。第一个牛栏里面肯定是要放牛的,因为牛栏的数目有限,牛又要都放进去,还要求最大的最小距离,如果你还浪费牛栏的话那肯定不行,mid就是每次二分得到的最小距离,然后进入判断,看第一个牛栏跟第二个牛栏之间的间隔与mid谁大,看放不放的下,放不下就要往后走,比较mid和第一个牛栏与第三个牛栏的距离,如果能放下,将牛数减一,下次就要比较第三个和第四个之间的距离了,所以我们要有一个标记记录最新的已经放牛的牛栏。直到把所有的牛放完或已经遍历完所有牛栏,如果牛没放完,则这个mid不行返回flase,如果放完了则要将mid再往大开一点(道理同上提思路)。

AC代码

#include<iostream>
#include<algorithm> 
using namespace std;
int a[100010];
int n,c;
int putcow(int mid)
{
    int cnt=1,p=a[1];//第一个牛栏一定要放牛
    for(int i=2;i<=n;i++)
    {
        if(a[i]-p>=mid)//两牛栏间距大于最小距离
        {
            cnt++;
            p=a[i];
        }
    }
    if(cnt>=c)
        return 1;
    else
        return 0;
}
int find()
{
    int left=0,right=a[n]-a[1],mid;
    while(left<=right)
    {
        mid = (left+right)/2;
       if(putcow(mid))
          left=mid+1;
       else 
          right=mid-1;
    }
    return right;
}
int main()
{
   while(scanf("%d%d",&n,&c)!=EOF)
   {
     int i;
     for(i=1;i<=n;i++)
     scanf("%d",&a[i]);
     sort(a+1,a+n+1);//牛量比较大来一手sort算了,免得时间超限
     printf("%d\n",find());
   }
}
发布了32 篇原创文章 · 获赞 5 · 访问量 900

猜你喜欢

转载自blog.csdn.net/LebronGod/article/details/104431679