两道题目都是基础二分的模板题,先看第一题,题意为总共有 n n n间牛舍, m m m头牛,要将 m m m头牛安排在 n n n间牛舍,为防止牛互相攻击,使两头牛之间的最小距离最大!最大! 最大!(重要的事情说三遍)
以样例来说明,
5 3
1 2 8 4 9
根据上面的样例,首先对牛舍进行排序,可以得到
1 2 4 8 9
可以有以下几种方案(将牛安排在以下编号的牛舍)
1
2
4
此方案牛与牛最小的距离为1
1
4
8
此方案牛与牛最小的距离为3
1
4
9
此方案牛与牛最小的距离为3
2
4
8
此方案牛与牛最小的距离为2
2
4
9
此方案牛与牛最小的距离为2
……
从以上方案可以看出,就是要尽可能的让最小距离最大。再来看看下面的代码
bool chk(int t){
int sum=a[1],ans=1;
for(int i=2;i<=n;i++){
if(sum+t>a[i]) continue;
else{
sum=a[i];
ans++;
}
}
return ans>=m;
}
通过样例的数据,对照上面的代码不难得到,
当t=1
的时候,ans=5;
当t=2
或者t=3
都可以满足ans=3;
当t>=4
的时候,ans=2;
因此,只有当t=2或者t=3
才能满足条件(样例共3头牛),显然,t=3才符合题意,那么,怎么确保t尽量取到更大的值呢?
可以这样分析,当ans>m的情况那么一定是二分值太小,而anst
取到更大的值应该采用if(chk(t)) l=mid
,这样求出的二分值更大。
while(l<r-1){
int mid=(l+r)/2;
if(chk(mid)) r=mid;
else l=mid;
}
如果把chk函数里面的返回值改成
return ans<=m
,配上if(chk(t)) r=mid;else l=mid
这样,表面上看没什么错误,实际上最终求出的二分值是更小的,这是不符合题意的。
本题要求将n个整数系列分成指定的m段,每段至少包含一个数,求处所有的分段方法中,各段的和的最大值最小! 最小! 最小!(重要的事情说三遍)
题目还有一个重要的要求,要求每段的数都是连续的,即按照原数的顺序切割成m段。
5 3
4 2 4 5 1
对照样例数据,可以采用如下集中切法:
4 2
|4 5
|1
三段之和分别为6
9
1
,和的最大值为9
4
|2 4
|5 1
三段之和分别为4
6
6
,和的最大值为6
以上两种切割的方法都是分成3段,但是,依据题意,更优的切割方法是第二种.
接下来看看本题正确的check函数的写法
bool chk(int x){
int sum=0,tot=1;
for(int i=1;i<=n;i++){
if(sum+a[i]<=x) sum+=a[i];
else{
tot++;
sum=a[i];
}
}
return tot<=m;
}
和第一题一样,考虑到存在tot=m
的情况下,其二分值不是最优的(样例中的第1种切割方法),应该尽量让二分值尽可能的小又同时满足tot=m
这个前提。
如果tot<=m
,说明是二分值偏大,因此可以采用 if(chk(x)) r=mid;else l=mid
,这样就可以满足tot=m
的同时,二分值最小。
while(l<r-1){
int mid=(l+r)/2;
if(chk(mid))r=mid;
else l=mid;
}
需要注意的是,本题的二分算法的初值必须让 l = m a x ( a i ) l=max(ai) l=max(ai), r = s u m ( a i ) r=sum(ai) r=sum(ai)。
如果把chk函数里面的返回值改成
return ans>=m
,配上if(chk(t)) l=mid;else r=mid
这样,表面上看没什么错误,实际上最终求出的二分值虽然满足了分成了m段,但是二分值却是更大的,(第一种切割方法就属于这种情况)这是不符合题意的。
#include
using namespace std;
int n,m,a[100008];
bool chk(int t){
int g=a[1],ans=1;
for(int i=2;i<=n;i++){
if(g+t>a[i]) continue;
else{
g=a[i];
ans++;
}
}
return ans>=m;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",a+i);
sort(a+1,a+n+1);
int l=a[1],r=a[n];
while(l<r-1){
int midl=(l+r)>>1;
if(chk(midl)) l=midl;
else r=midl;
}
if(chk(l)) printf("%d",l);
else printf("%d",r);
return 0;
}
#include
using namespace std;
int n,m,a[100005],maxx=-1,cnt;
bool chk(int x){
int sum=0,tot=1;
for(int i=1;i<=n;i++){
if(sum+a[i]<=x) sum+=a[i];
else{
tot++;
sum=a[i];
}
}
return tot<=m;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
maxx=max(maxx,a[i]);
cnt+=a[i];
}
int l=maxx,r=cnt;
while(l<r-1){
int mid=(l+r)/2;
if(chk(mid))r=mid;
else l=mid;
}
if(chk(l)) printf("%d",l);
else printf("%d",r);
return 0;
}