洛谷p2440 木材加工
题目描述
木材厂有 n根原木,现在想把这些木头切割成 k 段长度均为L的小段木头(木头有可能有剩余)。
当然,我们希望得到的小段木头越长越好,请求出 L 的最大值。
木头长度的单位是cm,原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。
例如有两根原木长度分别为 1111 和 2121,要求切割成等长的 66 段,很明显能切割出来的小段木头长度最长为 55。
今天水一篇文章,刚刚刷到这道题的时候,还在想他和二分啥关系,后来终于想清楚了,用来木材切割的最大长度不就是木材的总长去除以所需要的段数嘛,不过一般不可能这么巧的啦,通常要比这个最大的长度小,那么如何快速找到这个最适长度呢,那就是要对0~max这个区间进行二分查找了,我的二分查找如下:
bool check(int sum) //用来判断这个长度合不合适
{
int ans=0;
for(int i=0;i<n;i++)
{
ans+=p[i]/sum;
}
if(ans>=l)
{
return true;
}
else
return false;
}
void dp(int l,int r)
{
while(l<r)
{
int mid=(l+r+1)/2;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<r<<endl;
}
看到二分查找,就有最让人头疼的问题,这个边界怎么取啊?取偏左还是偏右的中间点啊…
我们可以这么想,假如,现在有两个数字,左边那个是合适的,而右边那个是不合适的,假如我们取偏左边的中间点,可以想象到待会那个check就会返回true,会发生什么?没错,那就是L边界右移了,可是刚刚已经说过,那个右边是不合适的,偏偏这个时候L和R就重叠在一起,就跳出循环,那么必定得到错误的结果;反过来,如果你取偏右的中间点,那么待会R就会回退,不信的话手动模拟一下。所以,我们需要取偏右的那个中间点,所以mid就要+1的意思。
最后,附上AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
int p[100009];
int n,l;
bool check(int sum)
{
int ans=0;
for(int i=0;i<n;i++)
{
ans+=p[i]/sum;
}
if(ans>=l)
{
return true;
}
else
return false;
}
void dp(int l,int r)
{
while(l<r)
{
int mid=(l+r+1)/2;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<r<<endl;
}
int main()
{
cin>>n>>l;
unsigned long long s=0;
for(int i=0;i<n;i++)
{
cin>>p[i];
s+=p[i];
}
dp(0,s/l);
}