糖果传递(思维,推式子)
题目:传送门
题意
有n个小朋友坐成一圈,每人有a[i]个糖果。
每人只能给左右两人传递糖果。
每人每次传递一个糖果代价为1。
求使所有人获得均等糖果的最小代价。
1≤n≤1000000
数据保证一定有解。
思路
设第一个人给了第 n 个人 x1 个糖果,第二个人给了第一个人 x2 个糖果,.....第 n 个人给了第 n - 1 个人 xn 个糖果。
答案就是 abs(x1) + abs(x2) + abs(x3) + ..... + abs(xn)
最终每个人的糖果数 result 是总的糖果数 s 除以人数 n,即 result = s / n;
对于第一个人来说有:
a[1] - x1 + x2 = result;
依次列出有:
a[2] - x2 + x3 = result;
a[3] - x3 + x4 = result;
....
化简一下有
x2 = x1 + result - a[1] = x1 - (a[1] - result);
x3 = x2 + result - a[2] = x1 - (a[1] - result) - (a[2] - result);
......
我们让
c[1] = a[1] - result;
c[2] = (a[1] - result) + (a[2] - result);
......
那么就有 xi = x1 - c[i - 1]; 则最终的答案就是 abs(x1) + abs(x1 - c[1]) + abs(x1 - c[2]) ..... + abs(x1 - c[n - 1]) ;
观察发现,abs(x1) + abs(x1 - c[1]) + abs(x1 - c[2]) ..... + abs(x1 - c[n - 1]) 类似于一维坐标上,所有点到某个定点的总距离和。
那么,我们可以对 c[i] 排序,定点为所有点中点时,距离和最小。
#include <bits/stdc++.h> #define LL long long #define ULL unsigned long long #define UI unsigned int #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF 0x3f3f3f3f #define inf LLONG_MAX #define PI acos(-1) #define fir first #define sec second #define lb(x) ((x) & (-(x))) #define dbg(x) cout<<#x<<" = "<<x<<endl; using namespace std; const int N = 1e6 + 5; int n, a[N]; LL c[N]; void solve() { scanf("%d", &n); LL result = 0; rep(i, 1, n) scanf("%d", &a[i]), result += a[i]; result /= n; rep(i, 1, n - 1) c[i] = c[i - 1] + a[i] - result; sort(c + 1, c + 1 + n); LL ans = 0LL; int pos = (n + 1) / 2; rep(i, 1, n) { ans += abs(c[i] - c[pos]); } printf("%lld\n", ans); } int main() { // int _; scanf("%d", &_); // while(_--) solve(); solve(); return 0; }