「一本通 1.1 练习 6」糖果传递

GitHub跳转原题关系图返回列表

环状均分纸牌经典问题,建立糖果流转平衡方程,通过代数变形将环上流动代价转化为数轴上一点到各点的曼哈顿距离之和,利用中位数贪心求得最小值。

OJ: roj

题目 ID: 1432

难度:普及+/提高-

标签:贪心中位数数学

创建: 2026-09-30 09:55

更新: 2026-10-07 11:01

形式化题目

给定一个长度为 nn 的环形序列 a1,a2,…,ana_1, a_2, \dots, a_n,满足所有数之和能够被 nn 整除。 每次操作可以在相邻元素间传递 11 单位数值,代价为 11。 求使序列中所有元素最终均等于平均数 aˉ=1n∑i=1nai\bar{a} = \frac{1}{n} \sum_{i=1}^n a_i 的最小总代价。

正解

思路

本题是经典的「环状均分纸牌」问题。

1. 变量定义与平衡方程

设第 ii 个人给第 i−1i-1 个人传递了 xix_i 颗糖果(其中第 11 个人向第 nn 个人传递 x1x_1 颗;若 xi<0x_i < 0,表示第 i−1i-1 个人向第 ii 个人传递了 −xi-x_i 颗)。

对于第 ii 个人,最终持有的糖果数应为平均数 aˉ\bar{a}:

  • 初始拥有 aia_i 颗;
  • 给了第 i−1i-1 个人 xix_i 颗;
  • 从第 i+1i+1 个人处得到了 xi+1x_{i+1} 颗。

由此可列出每个人的平衡方程:

ai−xi+xi+1=aˉ(1⩽i⩽n)a_i - x_i + x_{i+1} = \bar{a} \quad (1 \leqslant i \leqslant n)
其中下标循环,即 xn+1=x1x_{n+1} = x_1。

每传递一颗糖果的代价为 11,因此总代价为:

∑i=1n∣xi∣\sum_{i=1}^n |x_i|

2. 代数递推与转化

将平衡方程移项变形:

xi+1=xi−(ai−aˉ)x_{i+1} = x_i - (a_i - \bar{a})

依次展开所有变量,用自由变量 x1x_1 表示其余所有 xix_i:

x1=x1−0x_1 = x_1 - 0
x2=x1−(a1−aˉ)x_2 = x_1 - (a_1 - \bar{a})
x3=x2−(a2−aˉ)=x1−∑k=12(ak−aˉ)x_3 = x_2 - (a_2 - \bar{a}) = x_1 - \sum_{k=1}^2 (a_k - \bar{a})
…\dots
xi=x1−∑k=1i−1(ak−aˉ)x_i = x_1 - \sum_{k=1}^{i-1} (a_k - \bar{a})

令:

c1=0c_1 = 0
ci=∑k=1i−1(ak−aˉ)(2⩽i⩽n)c_i = \sum_{k=1}^{i-1} (a_k - \bar{a}) \quad (2 \leqslant i \leqslant n)

则有:

xi=x1−cix_i = x_1 - c_i

因此,目标函数可化简为:

∑i=1n∣xi∣=∑i=1n∣x1−ci∣\sum_{i=1}^n |x_i| = \sum_{i=1}^n |x_1 - c_i|

3. 几何模型:数轴上的中位数

上式在几何上等价于:在数轴上有 nn 个固定点 c1,c2,…,cnc_1, c_2, \dots, c_n,我们需要选择一个实数点 x1x_1,使得 x1x_1 到这 nn 个点的距离之和最小。

由绝对值函数的性质可知,x1x_1 取点集 cc 的中位数时距离之和取得最小值。

4. 样例图示推演

以输入样例为例:n=4n = 4,糖果数分别为 [1,2,5,4][1, 2, 5, 4]。

  • 总糖果数为 1+2+5+4=121 + 2 + 5 + 4 = 12,平均数 aˉ=3\bar{a} = 3。
  • 计算偏差 ai−aˉa_i - \bar{a}:[−2,−1,2,1][-2, -1, 2, 1]。
  • 构造差值序列 cc:
    • c1=0c_1 = 0
    • c2=c1+(a1−aˉ)=0+(−2)=−2c_2 = c_1 + (a_1 - \bar{a}) = 0 + (-2) = -2
    • c3=c2+(a2−aˉ)=−2+(−1)=−3c_3 = c_2 + (a_2 - \bar{a}) = -2 + (-1) = -3
    • c4=c3+(a3−aˉ)=−3+2=−1c_4 = c_3 + (a_3 - \bar{a}) = -3 + 2 = -1
  • 得到数组 c=[0,−2,−3,−1]c = [0, -2, -3, -1]。
  • 将 cc 排序后为 [−3,−2,−1,0][-3, -2, -1, 0]。
  • 取中位数 x1=c[4//2]=c[2]=−1x_1 = c[4 // 2] = c[2] = -1。
  • 计算总代价:∣−1−(−3)∣+∣−1−(−2)∣+∣−1−(−1)∣+∣−1−0∣=2+1+0+1=4|-1 - (-3)| + |-1 - (-2)| + |-1 - (-1)| + |-1 - 0| = 2 + 1 + 0 + 1 = 4。
flowchart LR
    c1["c3 = -3"] --- c2["c2 = -2"]
    c2 --- mid["x1 = -1 (中位数 c4)"]
    mid --- c3["c1 = 0"]

代码

Python 版:

python
"""环状均分纸牌 / 糖果传递:前缀和转化为数轴上一点到各点的曼哈顿距离最小(中位数)。"""
import sys

def main() -> None:
    data = iter(map(int, sys.stdin.buffer.read().split()))
    n = next(data)  # 题面的 n
    a = [next(data) for _ in range(n)]
    avg = sum(a) // n
    # c[i] = S[i] - i * avg, 其中 c[0] = 0
    # 代价为 sum(|x_1 - c[i]|),取 x_1 为 c 的中位数
    c = [0] * n
    s = 0
    for i in range(1, n):
        s += a[i - 1] - avg
        c[i] = s
    c.sort()
    mid = c[n // 2]
    print(sum(abs(x - mid) for x in c))

if __name__ == "__main__":
    main()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-05 00:48
 * update_at: 2026-10-05 00:48
 */
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1000005;

typedef long long ll;

ll n;
ll a[MAXN]; // 第 i 个小朋友原有的糖果数(下标从 0 开始)
ll c[MAXN]; // c[i] = sum_{k=0}^{i-1}(a[k] - avg),即前 i 个人的糖果偏差前缀和,c[0] = 0

void read_input() {
    cin >> n;
    for (ll i = 0; i < n; i++) {
        cin >> a[i];
    }
}

void solve() {
    // 先求平均数:总糖果数一定能被 n 整除。
    ll total = 0;
    for (ll i = 0; i < n; i++) {
        total += a[i];
    }
    ll avg = total / n;

    // 设第 1 个人顺时针传给第 n 个人的数量为自由变量 x,
    // 则与下一个人之间的传递量可表示为 x - c[i],
    // 总代价 = sum |x - c[i]|,取 x 为 c[] 的中位数时最小。
    c[0] = 0;
    for (ll i = 1; i < n; i++) {
        c[i] = c[i - 1] + (a[i - 1] - avg);
    }

    sort(c, c + n);
    ll mid = c[n / 2];

    ll ans = 0;
    for (ll i = 0; i < n; i++) {
        if (c[i] > mid) {
            ans += c[i] - mid;
        } else {
            ans += mid - c[i];
        }
    }
    cout << ans << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    read_input();
    solve();

    return 0;
}

复杂度

  • 时间复杂度:计算平均数与前缀和差值数组 cc 耗时 O(n)O(n);排序 cc 耗时 O(nlog⁡n)O(n \log n);累加曼哈顿距离耗时 O(n)O(n)。总时间复杂度为 O(nlog⁡n)O(n \log n)。在 n=106n = 10^6 时,Python 运行耗时约 0.2∼0.30.2 \sim 0.3 秒,完全在时限内。
  • 空间复杂度:存储原数组与差值数组 cc 占用 O(n)O(n) 空间。

总结

环状均分纸牌相比普通链状问题的难点在于闭环带来的流动自由度。通过设定相邻传递量的未知数并列出平衡方程,我们利用前缀和消元将 nn 个关联变量统一表达为 x1−cix_1 - c_i 的形式,成功将代数求和极值问题转化为数轴绝对值距离之和极小化问题,最终利用中位数贪心高效求得全局最优解。