前言
能 ( t dp) 就不要乱猜结论贪心。
题目
题目大意:
给出 (n) 个人的数轴上的坐标 (a_i),保证为偶数,每个人的速度至多为 (1),需要使得每一个 (iin[1,n-1]),第 (i) 个人与第 (i+1) 个人相遇过,求最小时间。
(1le nle 2 imes 10^5;0le a_ile 10^9;a_1<a_2<...<a_n.)
讲解
本做法时间复杂度为 (O(nlog_2n))。
首先二分一个时间 (t),考虑 ( t dp) 验证。
我们发现对于每个人,无非只有两种策略:
- 先向右走,碰到第 (i+1) 个人后向左走。
- 先向左走,碰到第 (i-1) 个人后向右走。
令 (dp_{i,0}) 表示先向右走,向右最多能走多远;(dp_{i,1}) 表示先向左走,向右最多能走多远。
注意这里定义的都是向右能走多远。特别的,(dp_{1,0}=dp_{1,1}=t)。
然后开始大力分类讨论。为了表述方便,我们将第 (i-1) 个人称为她,第 (i) 个人称为他。
1.她先向右走,他先向左走。
合法条件:由于她先向右走,所以在她走到右边尽头的时候他必须已经到达,否则无法碰面。
if(a[i-1]+dp[i-1][0] >= a[i]-dp[i-1][0])
显然一旦相遇,他就会往右走,所以有如下转移:
up(dp[i][1],t-(a[i]-a[i-1]));
ps: (up(x,y)) 表示 (x=max(x,y))。
2.她先向右走,他先向右走。
合法条件同上(可以视作他先向右走0步)。
此时他们的间距保持不变,而一旦她向左走,他就追不上她了,所以有:
up(dp[i][0],dp[i-1][0]-(a[i]-a[i-1])/2);
3.她先向左走,他先向右走。
合法条件:她在 (t) 秒末一定会在 (a_{i-1}+dp_{i,1}),所以此时他一定要能到此处才行。
if(a[i-1]+dp[i-1][1] >= a[i]-t)
他向右走可以支配的时间显然为总时间减去他到她右端点的时间,而向右走显然是个来回,所以还要除以二。
up(dp[i][0],(t-a[i]+a[i-1]+dp[i-1][1])/2);
4.她先向左走,他先向左走。
合法条件同上。
相遇后显然两人都应该向右走,所以有:
up(dp[i][1],dp[i-1][1]+a[i-1]-a[i]);
代码
int dp[MAXN][2];//0:go right first; 1:opposite
void up(int &x,int y){x = Max(x,y);}
bool check(int t)
{
for(int i = 2;i <= n;++ i) dp[i][0] = dp[i][1] = -INF;
dp[1][0] = dp[1][1] = t;
for(int i = 2;i <= n;++ i)
{
bool suc = 0;
//rl & rr
if(dp[i-1][0] >= (a[i]-a[i-1])/2)
{
up(dp[i][1],t-(a[i]-a[i-1]));
up(dp[i][0],dp[i-1][0]-(a[i]-a[i-1])/2);
suc = 1;
}
//lr && ll
if(a[i-1]+dp[i-1][1] >= a[i]-t)
{
up(dp[i][0],(t-a[i]+a[i-1]+dp[i-1][1])/2);
up(dp[i][1],dp[i-1][1]+a[i-1]-a[i]);
suc = 1;
}
if(!suc) return 0;
}
return 1;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read();
for(int i = 1;i <= n;++ i) a[i] = Read();
if(n <= 3) {Put((a[n]-a[1]) / 2);return 0;}
int ans = (a[n]-a[1]) / 2,l = 0,r = (a[n]-a[1])/2;
while(l <= r)
{
int mid = (l+r) >> 1;
if(check(mid)) r = mid-1,ans = mid;
else l = mid+1;
}
Put(ans);
return 0;
}
后记
第一次尝试用花里胡哨的方式写题解(指使用他和她),如有不清楚之处,还请见谅,可以在评论区留言。