uva 11300 Spreading the Wealth_数学推倒 + 思维

这道题和负载平衡问题是同一道题, 如果 n < = 100 n <= 100 的话是可以用最小费用流来求解的。
但是题中 n n 最大可达到 1 0 6 10^6 , 这就需要我们进行一些性质分析与推导。
首先, 我们设每个·人手里最终金币数为 C C
X i X_{i} 为第 i i 个人给第 i + 1 i+1 个人的金币数目, 这个数目可以为负(第 i + 1 i + 1 个人向左给了第 i i 个人 X i |X_{i}| 个。
则我们不难发现:

  1. A 2 + X 1 X 2 = C A_{2}+X_{1}-X_{2}=C
  2. A 3 + X 2 X 3 = C A_{3}+X_{2}-X_{3}=C
  3. A 4 + X 3 X 4 = C A_{4}+X_{3}-X_{4}=C
  4. A i + X i 1 X i = C A_{i}+X_{i-1}-X_{i}=C
    而这道题要求的其实就是 m i n X 1 + X 2 + X 3 + X 4 + . . . X n min|X_{1}| + |X_{2}| + |X_{3}| + |X_{4}| +... |X_{n}|
    那么,我们可将上面的等式进行变形,得:
  5. X 1 = X 1 X_{1} = X_{1}
  6. X 2 = A 2 C + X 1 X_{2} =A_{2}-C+X_{1}
  7. X 3 = A 2 + A 3 2 C + X 1 X_{3} =A_{2} + A_{3}-2*C+X_{1}
  8. X 4 = A 2 + A 3 + A 4 3 C + X 1 X_{4} =A_{2} + A_{3} +A_{4}-3*C+X_{1}
  9. X 5 = A 2 + A 3 + A 4 + A 5 4 C + X 1 X_{5} =A_{2}+A_{3}+A_{4}+A_{5}-4*C+X_{1}
    此时,相信聪明的读者们不难发现规律:
    X i = k = 2 i ( i 1 ) C + X 1 X_{i} = \sum\limits_{k=2}^i-(i-1)*C+X_{1} X i = k = 2 i ( i 1 ) C X 1 X_{i} = \sum\limits_{k=2}^i-(i-1)*C-(-X_{1})
    我们可以把 X 1 X_{1} 抽象成数轴上的一个点, 我们设 g i = k = 2 i ( i 1 ) C g_{i}= \sum\limits_{k=2}^i-(i-1)*C ,那么我们希望 X 1 X_{1} 到所有 g i g_{i} 的距离和最短,这个 X i X_{i} 一定是 g i g_{i} 中的中位数,于是我们将所有的 g i g_{i} 排序,取中位数作为 X 1 X_{1} 即可,我们也就能顺便推出所有的 X i X_{i} ,最后加和即可,总时间复杂度为 O ( n l o g n ) O(nlogn)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1000000 + 10;
int  A[maxn], n;
long long g[maxn], x[maxn], sumv[maxn], C, ans;
inline void init()
{
    memset(g,0,sizeof(g));
    memset(sumv, 0, sizeof(sumv));
    memset(x,0,sizeof(x));
    memset(A,0,sizeof(A));
    C = ans = 0;
}
int main()
{
    //freopen("input.txt","r",stdin);
    while(scanf("%d",&n) != EOF)
    {
        init();
        for(int i = 1;i <= n; ++i)scanf("%d",&A[i]), C += A[i],sumv[i] = A[i] + sumv[i-1];
        C /= n;
        for(int i = 2;i <= n; ++i) g[i] = (sumv[i] - sumv[1]) - (i - 1) * C;
        sort(g + 1, g + 1 + n);
        int pos = n / 2;
        if(n % 2 == 1) ++ pos;
        x[1] = -g[pos];
        for(int i = 2; i <= n; ++i) x[i] = A[i] + x[i-1] - C;
        for(int i = 1; i <= n; ++i) ans += abs(x[i]);
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/liyong1009s/article/details/82667619
今日推荐