Problem
A Communist regime is trying to redistribute wealth in a village. They have have decided to sit everyone around a circular table. First, everyone has converted all of their properties to coins of equal value, such that the total number of coins is divisible by the number of people in the village. Finally, each person gives a number of coins to the person on his right and a number coins to the person on his left, such that in the end, everyone has the same number of coins. Given the number of coins of each person, compute the minimum number of coins that must be transferred using this method so that everyone has the same number of coins.
The Input
There is a number of inputs. Each input begins withn(n<1000001), the number of people in the village.nlines follow, giving the number of coins of each person in the village, in counterclockwise order around the table. The total number of coins will fit inside an unsigned 64 bit integer.
The Output
For each input, output the minimum number of coins that must be transferred on a single line.
Sample Input
3
100
100
100
4
1
2
5
4
Sample Output
0
4
【思路分析】
乍一看像是DP问题,推了很久才发现是一个纯数学问题,关键是对问题的抽象。
有n个人围成一圈,每个人分有若干枚金币,设分别为a1,a2,......,an,一共有m枚金币,并且 m % n == 0,要求使得每个人的金币数相等的最少转移金币数。每个人的金币来自其左右两人,但是可以等效为来自其中一个人(类似于运动学里面的等效运动,因为金币数是“守恒”的),并且每个人最终的金币数一定为 m / n,令A = m / n,则有下列等式成立:
a1 + xn - x1 = A
a2 + x1 - x2 = A
a3 + x2 - x3 = A
.............
an + x(n - 1) - xn = A
其中xi为i给i + 1的金币数,由上式可以根据x1推导得到x2,x3.....xn,即用x1来表达其他位置的转移值,即:
x1 = x1
x2 = x1 - (A - a2)
x3 = x1 - (2 * A - a2 - a3)
...........
令bi为x(i + 1)等式中右边的常数项,即num = |x1 - b0| + |x1 - b1| + |x1 - b2| + .......+|x1 - b(n - 1)|。推到这里就可以看出num的最小值取决于在数轴上x1据b0,b1,b2,.....,b(n- 1)的距离之和。这个问题在初中就涉及过,当x1是b0,b1,b2,........,b(n - 1)的中位数时,num取得最小值。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 1000005
long long a[maxn],b[maxn];
int main()
{
int n;
while(scanf("%d",&n) != EOF)
{
long long sum = 0;
for(int i = 0;i < n;i++)
{
scanf("%lld",&a[i]);
sum += a[i];
}
long long A = sum / n;
b[0] = 0;
for(int i = 1;i < n;i++)
{
b[i] = b[i - 1] + A - a[i];
}
sort(b,b + n);
long long median = b[n / 2];//中位数
long long ans = 0;
for(int i = 0;i < n;i++)
{
ans += abs(median - b[i]);
}
printf("%lld\n",ans);
}
return 0;
}