[HAOI2008] 「糖果传递」 贪心
糖果传递问题
题目
『算法竞赛进阶指南』糖果传递
有n个小朋友坐成一圈,每人有a[i]个糖果。
每人只能给左右两人传递糖果。
每人每次传递一个糖果代价为1。
求使所有人获得均等糖果的最小代价。
思路

假设\(n\)个小朋友分别有\(a_1,a_2,...,a_n\)个糖果,第\(i\)个小朋友给\(i-1\)个小朋友\(x_i\)个糖果,(\(x_i\)可正可负)
那么目标就为\(\min\sum|x_i|\)。
对于上述的问题转化,可以列出方程组:
\(\begin{cases}a_1-x_1+x_2=\bar{a} \\ a_2-x_2+x_3=\bar{a}\\...\\ a_n-x_n+x_1=\bar{a}\end{cases}\)
进一步的化简为:
\(\begin{cases}x_1=x_1-(n\bar{a}-a_1-a_2-...-a_n)\\x_2=x_1-((n-1)\bar{a}-a_2-a_3-...-a_n)\\...\\x_{n-1}=x_1-(2\bar{a}-a_{n+1}-a_n)\\x_n=x_1-(\bar{a}-a_n)\end{cases}\)
注意:
化简过程是对于第i个式子,使用第i+1到第n个式子,例如对于第n-1项来说:
\(\begin{cases} a_{n-1}-x_{n-1}+x_{n}=\bar{a}\\ a_n-x_n+x_1=\bar{a}\end{cases}\)
将最后一个方程的\(x_n\)带入到倒数第二个方程中,把\(x_n=x_1-(\bar{a}-a_n)\)带入到\(a_{n-1}-x_{n-1}+x_{n}=\bar{a}\)中,得到\(x_{n-1}=x_1-(2\bar{a}-a_{n+1}-a_n)\)。以此类推
令右式常数分别为\(c_1,c_2,...,c_n\)
可以得到目标\(\min\sum|x_i|=(|x_1-c_1|+|x_1-c_2|+...+|x_1-c_n|)\),又因为\((|x_1-c_1|+|x_1-c_2|+...+|x_1-c_n|)\)等价于在数轴上找一个点\(x_1\),该点到\(c_1,c_2,...,c_n\)的距离之和最小。即转化为下面问题:
在一个数轴上,有偶数个点\(c_1,c_2,...,c_n\),需要找到一个点\(x_1\)使得\(x_1\)到其他点的距离最小,不难证明\(x_1\)为中位数(可以视作一个结论,证明见最下面)。

最后可以总结出算法
- 求出\(a\)平均数\(\bar{a}\)
- 求出\(c_1,c_2,...,c_n\)
- 求出其中的中点值\(x_1\)(对c数组排序)
- 计算\(|x_1-c_1|+|x_1-c_2|+...+|x_1-c_n|\)
其中第二个步骤求出c的数组使用了一下递推式:
\(\begin{cases}c_i=c_{i+1}+\bar{a}-a_i\\c_n =\bar{a}-a_n \end{cases}\)
递推式\(c_i=c_{i+1}+\bar{a}-a_i\)使用第\(i+1\)项方程减去第\(i\)项方程既可。
代码
// Author: oceanlvr
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
static int faster_iostream = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
return 0;
}();
const int maxn = 1e6 + 10;
ll n;
ll a[maxn];
ll abar;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], abar += a[i];
abar /= n;
a[n] = abar - a[n];
for (int i = n - 1; i >= 1; i--) a[i] = a[i + 1] + abar - a[i];
sort(a + 1, a + 1 + n);
ll p = (a[n / 2] + a[n / 2 + 1]) / 2;
ll res = 0;
for (int i = 1; i <= n; i++) {
res += abs(a[i] - p);
}
cout << res << endl;
return 0;
}
其他
『题目传送门』:acwing104
关于一个数轴上的序列\(c_1,c_2,...,c_n\),需要找到一个点\(x_1\)使得\(x_1\)到其他点的距离最小。
思路:首先对\(c\)排序,使得\(c_1 < c_2 < ... < c_n\),考虑\(c_1,c_n\),\(c_2,c_{n-1}\)...,\(c_{k-1},c_k\)两两配对,以\(c_1,c_n\)这一对来考虑

继续扩展到4个点,(两对)

推广到\(n=k\)的情况下(\(k=n/2\))同理,因此可知\(x\)应该取到\(\frac{x_k+x_{k+1}}{2}\)。

浙公网安备 33010602011771号