P8660 题解
P8660 题解
题目-P8660 [蓝桥杯 2017 国 A] 区间移位
题意
如果你没看懂题:
你可以位移这些遮阳伞,使得空地都可以被遮住而不被太阳照射到 需要使**移动最远**的那个遮阳伞**位移的距离最小**
主体思路:二分
涉及到最大值最小或是最小值最大的问题,一般都是通过二分答案来解决的。
我们简要地证明一下:
如果遮阳伞最大位移是
如果遮阳伞最大位移是
故答案具有单调性,也有两段性,可以二分答案。
二分板子
int binarySearch(){
int l,r,mid;
l=0,r=maxr;
while(l<r){
mid=l+(r-l)/2;
if(check(mid))r=mid;
else l=mid+1;
}
return l;
}
这里是整数二分的板子,但是答案会有小数怎么办。
其实很容易想到,当答案出现小数时,要想使得最大值最小,小数部分必须是
贪心
接下来的问题就是,check() 函数要怎么写。
我们可以用
于是,我们在每次增加一个区间的时候,使右端点
分类讨论,一个区间和
1.区间左端点 > t
这时候,我们要先判断该区间能否覆盖到点
如果能在满足最大位移为
如果不能,我们就不管它(记住这句话,后面要考)。
2.区间左端点 =t
这种情况非常简单,我们直接更新
3.区间左端点 < t
对于这种情况,如果想要把右端点尽可能地右,就得让区间位移尽可能地大。
但是这个大也有限度:不超过最大位移
注意:如果区间右移后右端点仍未超过
t ,那我们也不会采用它。代码中用\max() 实现。
那我们就可以开始愉快地写代码
并不!
现在又有新的问题:如何排列所有区间?
排列区间
方案 1:按左端点排序
也许你会觉得左端点靠左的就先判断,其实一开始我也是这么想的
看一组数据:
输入
4
0 10
12 14
11 20
21 10000
这里我们当然是选择把区间
如果按照左端点排序,就会先把区间
方案 2:按右端点排序
所以正解是按右端点排序。
可能有人 其实还是我 会想到 hack 数据,那我顺便也讲一下为什么按右端点排序可以解决刚刚按左端点排序出现的问题。
输入
4
0 10
17 19
11 20
21 10000
看到这个数据第一眼,如果按右端点排序,会先处理区间
当最大位移
二分之后
因此我们会跳过它,直接考虑区间
愉快写代码
铺垫了这么多,终于到代码实现了,太爽了!
注意一些代码实现细节
- 用结构体存储区间,再自定义排序
- 采用
vector数组存储没有开发过的区间,当然开个vis[maxn] 来表示是否处理过也可以,本人喜欢 STL,主要是懒
#include<bits/stdc++.h>
using namespace std;
#define maxn 10010
int n;
//存储区间:
struct st{
int l,r;
};
vector<st> D;
//优先按右端点排序:
bool cmp(st x,st y){
if(x.r!=y.r)return x.r<y.r;
else return x.l<y.l;
}
//验证答案:
bool check(int x){
int t=0;
vector<st> d(D);//复制一份用以删除用过的区间
while(true){
bool flag=0;//找没找到
for(int i=0;i<d.size();i++){
if(d[i].l>t){//需要把区间左移
if(d[i].l-x<=t){//可以左移
flag=1;
t+=d[i].r-d[i].l;
d.erase(d.begin()+i);//删除
i--;//删除后i要退1
break;
}
}else if(d[i].l==t){//区间左端点刚好是t点
flag=1;
t=d[i].r;
d.erase(d.begin()+i);
i--;
break;
}else{//区间已覆盖t点
flag=1;
t=max(t,d[i].r+min(x,t-d[i].l));//让更新后的t点尽可能地右(但是受到最大位移和覆盖到点t的限制)
d.erase(d.begin()+i);
i--;
break;
}
}
if(flag==0||d.empty())break;//没找到或者找完了就跳出
}
return t>=20000;
}
//二分板子
int binarySearch(){
int l,r,mid;
l=0,r=2*maxn;
while(l<r){
mid=l+(r-l)/2;
if(check(mid))r=mid;
else l=mid+1;
}
return l;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n;
for(int i=1,dl,dr;i<=n;i++){
cin>>dl>>dr;
D.push_back({2*dl,2*dr});//小数优化
}
sort(D.begin(),D.end(),cmp);
cout<<binarySearch()/2.0;//前面小数优化,这里记得除回去
}
愉快地 AC 了
感谢观看!!!