原题传送门:Jumping Through Segments - 洛谷
题目大意
数轴上有 n 条线段,第 i 条线段的范围是
开始时在原点,每次可以跳至多 k 的距离,但是在第 i 次跳跃后一定要站在第 i 条线段上。
求出最小的 k 使得你可以跳到第 n 条线段上。
思路
看到 “最小的” 三个字,有一些经验的朋友应该都能想到二分,
即二分答案(最小的k),判断k等于该值是是否能到第i条线段。
思路大家都能想得到,那怎么搜呢?
对于第i轮,我们维护其能够到达的最左边的点和最右边的点。例如:
图中我们已经求出了上一步所能达到的两端。
由于第i条线段要求在图中红点之间,所以到达第i条线段后的最左端应该是 ,即左侧红点位置。
同样的,如果出现这种情况:
这也就意味着,这一轮移动之后,最多只能到达右侧红点的位置,而左侧不受影响。
AC代码
于是,我们就有了这样简洁的代码:
#include<iostream>
using namespace std;
#define For(i, j, k) for(int i = j; i <= k; i++)
#define MaxN 200005
int t, n, l[MaxN], r[MaxN];
int check(int k){
int lpos = 0, rpos = 0; //当前最左端和最右端能到达的最远点
For(i, 1, n){
lpos -= k, rpos += k; //按照距离更新
if(lpos > r[i] || rpos < l[i]) return 0; //如果两条线段没有交集则不能到达
lpos = max(lpos, l[i]); //按要求更新答案
rpos = min(rpos, r[i]); //(因为这一轮结束后只能站在第i条线段上)
}
return 1;
}
int main()
{
cin >> t;
while(t--){ //还是多测
cin >> n;
int Max = 0;
For(i, 1, n) {
cin >> l[i] >> r[i];
Max = max(Max, r[i]); //最大的k即最远要求到达的点
}
int L = 0, R = Max, ans = Max;
while(L <= R){ //二分
int mid = (L + R) / 2;
if(check(mid)){ //合法
ans = mid; //记录答案,继续往小找
R = mid - 1;
}else{ //不合法,加大假设值
L = mid + 1;
}
}
cout << ans << endl;
}
return 0;
}
总结
二分不难想到,但毕竟是CF,在搜索上采用的方法确实需要巧妙。
总体难度橙到黄吧,可以作为经典的二分题目练练手。
好了,以上就是这篇文章的全部内容了。最后,如果你觉得这篇文章还不错或者对你有帮助,麻烦点个收藏点个赞,这是免费的,你随时可以取消。你的支持是作者最大的动力,谢谢!