题目背景
要保护环境
题目描述
木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头(木头有可能有剩余),需要得到的小段的数目是给定的。当然,我们希望得到的小段木头越长越好,你的任务是计算能够得到的小段木头的最大长度。木头长度的单位是cm。原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。
例如有两根原木长度分别为11和21,要求切割成到等长的6段,很明显能切割出来的小段木头长度最长为5.
输入格式
第一行是两个正整数N和K(1 ≤ N ≤ 100000,1 ≤ K ≤ 100000000),N是原木的数目,K是需要得到的小段的数目。
接下来的N行,每行有一个1到100000000之间的正整数,表示一根原木的长度。
输出格式
能够切割得到的小段的最大长度。如果连1cm长的小段都切不出来,输出”0”。
输入输出样例
输入 #1
3 7
232
124
456
输出 #1
114
题解
没想到二分法还有这么多变种的题目。我一直认为它只是一个小的知识点而已。
不过它的通用性还挺强的,碰到一个查找类的题都可以按这个思路优化以下。
这题就是练二分的入门题。
我先用了暴力。
代码
#include<bits/stdc++.h>
using namespace std;
int a[200000];
int main()
{
int N,K;
cin>>N>>K;
for(int i=1;i<=N;i++) cin>>a[i];
int ans=0;
for(int i=1; ;i++) //段的长度
{
int cou=0;
for(int j=1;j<=N;j++)
{
cou += a[j]/i;
if(cou>=K) break;
}
if(cou>=K)
{
ans = i;
}
else
break;
}
cout<<ans<<endl;
return 0;
}
思路就是从段长最小的1一直增大了切,直到切的数量达不到K,那么上一个达到的就是最大段长。
最后,果然..
TLE 了一个点。
于是考虑二分法。
二分关键就在于找low和high这两端。
low明显就是1开始。
high,考虑最大。经过考虑,对于读入大量原木长度,若选取其中原木最大值,就很接近high的最大了。但最大在比那个最大值还要大的地方,也就是根本没有木段能切除那个长度(因为长度不够),此时切出的长度为0。
ac代码
#include<bits/stdc++.h>
using namespace std;
int a[200000];
int max_duan=0;
int N,K;
int tongji(int mid)
{
int cou=0; //单个记数
for(int j=1;j<=N;j++)
{
cou += a[j]/mid;
if(cou>=K) break;
}
return cou;
}
int main()
{
cin>>N>>K;
for(int i=1;i<=N;i++)
{
cin>>a[i];
if(a[i]>max_duan) max_duan = a[i]; //找最大长度
}
int ans=0,cou=0;
int low=1,high=max_duan+1; //比最大长度还大(铁为0)
int mid;
while(low<=high)
{
mid = (low+high)/2; //中间段长
cou = tongji(mid);
if(cou>=K) //这里分两种情况
{
if(tongji(mid+1)>=K) low = mid+1;
else
{
ans = mid;
break;
}
}
else
{
high = mid-1;
}
}
cout<<ans<<endl;
return 0;
}