BZOJ 1045: [HAOI2008] 糖果傳遞 數學,亂搞

just_sort發表於2017-01-21

Description

  有n個小朋友坐成一圈,每人有ai個糖果。每人只能給左右兩人傳遞糖果。每人每次傳遞一個糖果代價為1。
Input

  第一行一個正整數n<=987654321,表示小朋友的個數.接下來n行,每行一個整數ai,表示第i個小朋友得到的
糖果的顆數.
Output

  求使所有人獲得均等糖果的最小代價。
Sample Input
4

1

2

5

4
Sample Output
4

解題方法: 設i順時針傳給下一個人的數量為xi,目標平均值值為ave。則目標就是讓ai−xi+x(i-1)−1=ave的前提下最小化。
這裡寫圖片描述

可以構造出
a1−x1+x2=ave
a2−x2+x3=ave
a1−x3+x4=ave
…..
an−xn+x1=ave

這個方程組有n個方程和n個變數,但顯然,最後一個方程式無意義。
於是考慮:
a1−x1+x2=ave⟹x2=ave+x1−a1
a2−x2+x3=ave⟹x3=ave+x2−a2=2×ave−a1−a2+x1
a3−x3+x4=ave⟹x4=3×ave−a1−a2−a3+x1

記c[i]=(sigma(a[j]),j從到i)-i*ave
所以:

x2=x1-c1
x3=x1-c2
….

我們希望Xi的絕對值之和儘量小,即|x1| + |x1-c1| + |x1-c2| + ……+ |x1-c(n-1)|要儘量小。注意到|x1-ci|的幾何意義是數軸上的點x1到xi的距離,所以問題變成了:給定數軸上的n個點,找出一個到他們的距離之和儘量小的點,而這個點就是這些數中的中位數,證明略。上面的c是直接替換之後的結果,也是下面程式碼裡面的c,畫一下就明白了。(0,c1,c2,c3”’,c[n-1])的中位數。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
int a[N], c[N];
int n;
int main(){
    scanf("%d", &n);
    long long ave = 0;
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        ave += a[i];
    }
    ave /= n;
    for(int i = 2; i <= n; i++){
        c[i] = c[i-1] + a[i] - ave;
    }
    sort(c + 1, c + n + 1);
    long long ans = 0;
    int j = (n + 1) / 2;
    for(int i = 1; i <= n; i++){
        ans += abs(c[i] - c[j]);
    }
    printf("%lld\n", ans);
    return 0;
}

相關文章