122. 糖果传递
题目链接
122. 糖果传递
有 \(n\) 个小朋友坐成一圈,每人有 \(a[i]\) 个糖果。
每人只能给左右两人传递糖果。
每人每次传递一个糖果代价为 \(1\)。
求使所有人获得均等糖果的最小代价。
输入格式
第一行输入一个正整数 \(n\),表示小朋友的个数。
接下来 \(n\) 行,每行一个整数 \(a[i]\),表示第 \(i\) 个小朋友初始得到的糖果的颗数。
输出格式
输出一个整数,表示最小代价。
数据范围
\(1≤n≤1000000,\)
\(0≤a[i]≤2×10^9,\)
数据保证一定有解。
输入样例:
4
1
2
5
4
输出样例:
4
解题思路
贪心
均分纸牌的环形情况,而存在一种最优方案,两数之间不用交换,可以当作将普通均分纸牌首尾连起来的逆过程。假设当前数 \(i\) 给 \(i+1\) 的贡献为 \(x_i\),其中 \(x_i\) 为负数时也没有关系,因为可以认为在之前 \(i\) 从\(i-1\) 拿了足够的数,即最终答案为 \(\sum_{i=1}^n|x_i|\),而有如下方程:
\[\left\{\begin{matrix} x_n+a_1-x_1=avg \\ x_1+a_2-x_2=avg \\ ... \\ x_{n-1}+a_n-x_n=avg \end{matrix}\right. \]最终答案用 \(x_1\) 表示为 \(\sum_i^n|s[i]-(i-1)\times avg+x_1|\),其中 \(s[i]\) 为将 \(a_1=0\) 时的前缀和,此最小值即转化为仓库选址问题,\(-x_1\) 为中位数时即为答案
- 时间复杂度:\(O(n)\)
代码
// Problem: 糖果传递
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/124/
// Memory Limit: 64 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair PII;
template bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e6+5;
int n,a[N],b[N];
int main()
{
cin>>n;
LL sum=0,avg=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
avg=sum/n;
for(int i=2;i<=n;i++)
b[i]=b[i-1]+a[i]-avg;
nth_element(b+1,b+(n+1)/2,b+1+n);
LL res=0;
for(int i=1;i<=n;i++)res+=abs(b[i]-b[(n+1)/2]);
cout<