UVa 11300 Spreading the Wealth 分金币

来源:互联网 发布:平平无奇 古天乐 知乎 编辑:程序博客网 时间:2024/06/06 00:14

题意:n个人坐成一圈,编号依次为1-n,1和n是邻座。每个人有一定数量的金币,保证金币总数能被n整除。现在要求每个人给相邻的两人各若干金币,使得所有人都持有等量金币。求所有人给出的金币的数量和的最小值。




这是一道数学题。我们这样考虑,对于某两个相邻的人,如果第一个人给了第二个人x个金币,而第二个人又给了第一个人y个金币,这相当于第一个人给了第二个人x-y个金币(当然,是负数也无妨,是负数相当于他得到金币)而第二个人没有给第一个人任何金币。如此看来,我们不妨设每个人只给他的下一个人金币(是负数就表示得到)。题目说了总数一定是n的倍数,则每个人最后的金币数为定值,不妨记为average。下面我们设每个人开始有Ai个金币,第i个人给第i + 1个人Xi个金币。我们可以列出方程:

A1 + X0 - X1 = average

A2 + X1 - X2 = average

A3 + X2 - X3 = average

......

An-1 + Xn-2 - Xn-1 = average

A0 + Xn-1 - X0 = average

当然最后这个方程并没有用,因为前n - 1个方程相加就可以得到这个方程


但是我们可以把每个Xi都用X1表示出来


下面对上述方程进行形如Xi = f(X1)形式的变形,可以得到:


X1 = X0 + A1 - average

X2 = X1 + A2 - average = X0 + A1 + A2 - 2*average

X3 = X2 + A3 - average = X0 + A1 + A2 - 3*average

......

Xn-1 = Xn-2 + An-1 - average = X0 + A1 + A2 + ...... + An-1 - average


若定义一个S数列,其中S0 = 0,Si = Si-1 + average - Ai,则有Si = X0 + A1 + A2 + ...... + Ai - i*average

代入上述变形即可得到

Xi = X0 - Si

于是我们要求的即为sigma(Xi) = sigma(X0 - Si)

这个式子的几何意义非常明显,就是给定数轴上的若干点S0、S1、S2 ...... Sn-1,要找一个点到这些点的距离和最小。

找这个点很简单。我们随便找个点x,设给定的若干点里位于x左边的点有L个,位于右边的点有R个,不放假设L > R,那么我们将x往左移动d个距离(不跨过x左边第一个点),则总距离和减少了L*d,增加了R*d,而L*d > R*d,所以移动后的点x更优。当L < R的时候亦是如此。因此我们必须保证对于找到的点x,位于x左边的给定的点和位于x右边的给定的点一样多。

于是我们将S0 ~ Sn-1排序后,要找的这个点一定是中间点(若总数是奇数则是正中间那个点,若总数是偶数则是中间两个点之间的任何一个点)。于是我们取这个点为Sn/2即可(对于总数是奇数或偶数都适用)。



#include <iostream>#include <cstdio>#include <cmath>#include <cstring>#include <algorithm>#include <vector>#include <map>#define ll long longusing namespace std;const int MAX = 1000005;int n;ll money[MAX], sum[MAX];void input(){    for(int i = 0; i < n; i++)        scanf("%d", &money[i]);}void solve(){    ll average = 0;    for(int i = 0; i < n; i++)        average += money[i];    average /= n;    sum[0] = 0;    for(int i = 1; i < n; i++)        sum[i] = sum[i - 1] + average - money[i];    sort(sum, sum + n);    int mid = n/2;    ll ans = 0;    for(int i = 0; i < n; i++)        ans += labs(sum[mid] - sum[i]);    printf("%lld\n", ans);}int main(){    while(scanf("%d", &n) != EOF)    {        input();        solve();    }    return 0;}


0 0
原创粉丝点击