[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}\)

posted @ 2020-03-16 15:35  AdaMeta730  阅读(152)  评论(0)    收藏  举报