題目描述
題目大意
有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中左右移動時,會使得總距離要麼向左邊方向增大,要麼向右邊方向增大,在其中間存在一個點取得最優方案。可以發現,這個總距離隨座標變化的圖像是一個單峯曲線。因此,我們也可以使用三分法求解該問題。