题目:https://www.51nod.com/Html/onlineJudge/questionCode.html#!problemId=1243
思路:对于最大值最小的问题可以直接去想:能不能用二分+贪心的思想去做。
这里就可以直接使用这种方法去做,首先对于二分的上限和下限:上限r就是码头的宽度-船的宽度,下限l就是所有的船刚好可以接在柱子上就是0。
对于判断函数:找出每个点可能在的最左和最右的位置,如果存在最左>最右,就说明这个值是不合理的换句话说:如果最长绳长如果小于等于这个值船就会出现重叠,这肯定是不行的,所以返回false提高上限至mid+1;而对于满足条件的点则可以将上一个点的坐标pos更新为l。最后如果没有出现不合理的情况的话返回true,说明当前的绳长是满足情况的则可以直接将上限变为当前值;反复下去则可以直接逼近我们想要的最大值最小的结果。
代码及补充:
bool can(vector<int>& v,int L,int x,int m){
//这里的pos是直接从第一个点开始的,因为第一个点要考虑到墙壁的影响,所以要特判
int pos=max(v[1]-L,x);
//第一个点之后也要判断是否合理!(把这个删掉刚好wa一个测试案例,别问我怎么知道的...)
if(pos>min(m-x, v[1]+L)) return false;
for(int i=2;i<(int)v.size();i++){
//前一个点的位置加船与船之间的距离和绳长最远可以蔓延到哪取最大值
int l=max(pos+2*x,v[i]-L);
//当前点加上绳长和墙壁边边取最小值
int r=min(m-x,v[i]+L);
if(l>r)return false;
//为了让后面的船有足够的空间,第一艘船应该尽量放在区间的左端点
pos = l;
}
return true;
}
void solve() {
int n,x,m;
cin>>n>>x>>m;
vector<int> v(n+1);
for(int i=1;i<=n;i++){
cin>>v[i];
}
//如果码头容不下所有船只的话直接输出-1
if(n*x*2>m){cout<<-1<<endl;return; }
int l=0,r=m;
while(l<r){
int mid=(l+r)>>1;
if(can(v,mid,x,m)){
r=mid;
}else{
l=mid+1;
}
}
cout<<l<<endl;
}总结:遇到最大值最小问题一定要先想到贪心+二分!先确定上下限,然后推出判断函数即可。
评论