信息学奥赛一本通 贪心算法
【题目描述】
有nn个小朋友坐成一圈,每人有aiai个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为1。
【输入】
第一行一个正整数n≤1000000,表示小朋友的个数.
接下来n行,每行一个整数ai,表示第i个小朋友得到的糖果的颗数.
【输出】
求使所有人获得均等糖果的最小代价。
【输入样例】
4
1
2
5
4
【输出样例】
4
【分析】
本题网上有许多题解,这里对于一些细节做一下阐述,以方便理解代码。
用x1...xn表示i小朋友给左边小朋友的糖果数量,正数为给予,负数为索取。根据分析可以列出一系列式子。
当前小朋友手头的糖果 - 给左边的糖果 + 右边给的糖果 = 平均值
a1 - x1 + x2 = average ==> x2 = x1 + average - a1 ==> x2 = x1 - (a1 - average)
a2 - x2 + x3 = average ==> x3 = x2 + average -a2 ==> x3 = x2 - (a2 - average)
......
最终可以写成如下的式子: 在数组c中存放了x1减号后面的一系列值,第一个数是0
x1 = x1 - 0 ==>c[1] = 0
x2 = x1 - (a1 - average) ==>c[2] = c[1] + a[1] - average
x3 = x1 - (a1 -average + a2 - average) ==>c[3] = c[2] + a[2] - average
......
xn = x1 - (a1 + ... an-1 - (n-1)*average) ==>c[n] = c[n-1] + a[n-1] - average
c[1] = 0;
for(int i = 2; i <= n; i++){
c[i] = c[i-1] + a[i-1] - average;
}
x1 + x2 + x3 +...+ xn的值最小,即移动代价最小才符合题目要求,题目求解转化为求点到所有点之间的距离和。先排序,取中位数点进行计算。
int mid = c[n / 2 + 1];
//求点到所有点之间的距离和
for(int i = 1; i <= n; i++){
ans += abs(c[i] - mid);
}
【完整代码】
#include <bits/stdc++.h>
long long a[1000005],c[1000005];
using namespace std;
int main(int argc, char *argv[]) {
int n;
long long sum = 0;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
sum += a[i];
}
//计算平均值
int average = sum / n;
/*
x1...n表示i小朋友给左边小朋友的糖果数量,正数为给,负数为索取
当前小朋友手头的糖果 - 给左边的糖果 + 右边给的糖果 = 平均值
a1 - x1 + x2 = average ==> x2 = x1 + average - a1 ==> x2 = x1 - (a1 - average)
......
......
最终可以写成如下的式子: ==>在数组c中存放了x1减去的一系列值,第一个数是0
x1 = x1 - 0 ==>c[1] = 0
x2 = x1 - (a1 - average) ==>c[2] = c[1] + a[1] - average
x3 = x1 - (a1 -average + a2 - average) ==>c[3] = c[2] + a[2] - average
......
xn = x1 - (a1 + ... an-1 - (n-1)*average) ==>c[n] = c[n-1] + a[n-1] - average
*/
c[1] = 0;
for(int i = 2; i <= n; i++){
c[i] = c[i-1] + a[i-1] - average;
}
/*
x1 + x2 + x3 +...+ xn的值最小,即移动代价最小才符合题目要求,转化为求点到所有点之间的距离和。
先排序,取中位数点进行计算。
*/
//升序
sort(c+1, c + n +1);
long long ans = 0;
//队列的中位数
int mid = c[n / 2 + 1];
//求点到所有点之间的距离和
for(int i = 1; i <= n; i++){
ans += abs(c[i] - mid);
}
cout << ans;
return 0;
}