题目描述
题目大意
有n个点排成一排,给出了它们的位置坐标,现在需要将它们按照相同的间隔k重新排列,问所有点需要移动的距离之和的最小值是多少?
分析过程
通过观察,可以发现两个小结论:
结论1 重新调整之后的排列至少存在一个位置与调整之前的相应位置重合。
可以用反证法证明:
设原序列(升序排列之后的)集合为A,我们假设已经获得了一个满足最优方案的序列集合B,若对于
均有
,此时,有3种情况:若A中的位置点 ai 大部分位于其对应位置bi的左侧时,此时我们将B向左移动一个单位必然可以使得结果更优(左边对减少距离的贡献更大);反之,向右移动更优,以上这两种情况均与假设中的“最优方案”矛盾;若A中的位置点均匀的散布在最优方案点的两侧,那么此时我们必然可以通过移动B而使得A与B之间存在相交点。综上所述,结论1得证。
结论2 由结论1的证明过程可以看出,在原序列中必然存在一个基准位置ai,当将序列a1,a1+k,a1+2k,…,a1+(n-1)k 中的对应位置(即 a1+(i-1)k)和 ai 对齐时,此时序列A中的点相对于间隔为k的序列的相应位置点来说,左右散布是均匀的,此时我们就得到了最优方案,即题中所求。
有了以上2个分析得到的结论做支撑之后,我们的问题就转化为了如何在原序列A中寻找基准位置。很显然一个一个枚举的时间复杂度是O(n2),必然要TLE。通过观察,我们发现,当把原序列A中的第一个位置作为基准位置时(选别的位置也行,但选第一个最方便),我们从左到右遍历一遍,将原序列A与标准序列a1,a1+k,a1+2k,…,a1+(n-1)k 分别作差。此时,差值处于中间的那个位置便是基准位置。(因为此时该位置能够均匀的将A序列各位置散布在a1,a1+k,a1+2k,…,a1+(n-1)k 序列位置点的左右两侧)
综上,我们得到的做法为:先将原序列x进行升序排列,与标准序列a1,a1+k,a1+2k,…,a1+(n-1)k 分别做差,最后找中间值确定基准位置,最后计算结果。时间复杂度为
附AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 1000;
ll x[maxn];
int main(){
ll n,k,i,ans = 0;
ios::sync_with_stdio(false);
cin>>n>>k;
for(i=1;i<=n;++i) cin>>x[i];
sort(x+1,x+n+1);
//判断在左对齐的情况下,每个点和标准点的相对位置(求差),符号为正说明在标准点右侧,否则在左侧
for(i=2;i<=n;++i){
x[i] -= (i - 1) * k;
}
sort(x+1,x+n+1);
ll mid = x[(1+n)/2];
for(i=1;i<=n;++i) ans += abs(x[i] - mid);
cout<<ans;
return 0;
}
Another Solution
如上文所述,实际上标准排列a1,a1+k,a1+2k,…,a1+(n-1)k在原序列A中左右移动时,会使得总距离要么向左边方向增大,要么向右边方向增大,在其中间存在一个点取得最优方案。可以发现,这个总距离随坐标变化的图像是一个单峰曲线。因此,我们也可以使用三分法求解该问题。