环状均分糖果:设 c[i] 为 i-1 传给 i 的净流量,用平衡方程推出 c 是 a 减均值的前缀和,总代价为 Σ|c[i]-x|,排序取中位数即最小值。
OJ: roj
题目 ID: 3026
难度:普及+/提高-
标签:贪心中位数数学python
创建: 2026-10-01 10:51
更新: 2026-10-07 13:50
形式化题目
正解
思路
朴素做法及其瓶颈
最直接的想法是贪心地模拟传递:每次找一对"多的人"和"少的人",沿环上较短方向传糖果。但"较短方向"在环上与整体流量耦合——一个人的糖果可能绕圈流过多个中转者,每一步局部选择都影响后面的流向,模拟既难写对又容易陷入
关键转化:只数"经过的糖果",不模拟过程
无论传递如何进行,最终每人手里都是平均数
第
这是一个递推式:整条环的流量只由自由变量
注意到
问题变成:在数轴上选一个点
用样例验证
以样例
| 人 |
1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 初始 |
1 | 2 | 5 | 4 |
| 排序后 |
排序后中位数在
| 边(谁给谁) | ||||
|---|---|---|---|---|
| 净流量 |
即 4 号给 1 号 2 颗,3 号给 2 号、3 号给 4 号各 1 颗(负号表示反向),总代价
于是算法只有三步:求
代码
Python 版:
import sys
def main() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
a = [next(data) for _ in range(n)]
s = sum(a) // n # 目标:每人最终 s 颗
from itertools import accumulate
# c[i] = a[i]-s + c[i-1]:即 i-1 传给 i 的净糖果数(负表示反向传)
c = list(accumulate(x - s for x in a))
c.sort() # 代价 = Σ|c[i]-x|,x 取中位数最优
m = c[n // 2]
print(sum(abs(v - m) for v in c))
main()C++ 版(同一算法):
/**
* 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-06 14:42
* update_at: 2026-10-06 14:42
*/
// 糖果传递:环上均分糖果,最小化相邻传递总代价。
// 设 c[i] 为前 i 个人的 (a[i]-s) 前缀和,即第 i 人传给第 i+1 人的净流量
//(负号表示反向传),总代价 = Σ|c[i] - x|,x 取 c 的中位数时最小。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
typedef long long ll;
ll n;
ll a[MAXN]; // a[i]:第 i 个小朋友的初始糖果数
ll c[MAXN]; // c[i]:(a[i]-s) 的前缀和,即边上净流量关于 x_1 的偏移
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
ll sum = 0;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i];
}
ll s = sum / n; // 最终每人手里的糖果数(数据保证有解,即 n 整除 sum)
// 前缀和:c[i] = c[i-1] + a[i] - s,第 n 项恰好回到 0(环闭合)
for (ll i = 1; i <= n; i++) {
c[i] = c[i - 1] + a[i] - s;
}
// 总代价 = Σ|c[i] + x_1|,数轴上选 -x_1 到各点距离和最小 -> 取中位数
sort(c + 1, c + n + 1);
ll mid = c[(n + 1) / 2]; // 中位数(n 为奇数取正中,偶数取任意一个皆最优)
ll ans = 0;
for (ll i = 1; i <= n; i++) {
ans += llabs(c[i] - mid);
}
cout << ans << endl;
return 0;
}复杂度
- 时间复杂度
:一遍前缀和 ,排序 (也可用 nth_element类思想做到期望)。 - 空间复杂度
:存储数组 和前缀和数组 。
总结
- 环上均分问题不要模拟传递过程,而是给每条边设净流量变量
,用守恒方程 把所有流量表示成 的线性函数。 - 代数变形后总代价是
,其中 是 的前缀和——环问题就此变成数轴上的"选一点到各点距离和最小",即中位数贪心。 - 三个可复用的套路:把长距离传递拆成相邻传递(代价不变)、环上流量只有一个自由变量、绝对值和的最优解在中位数。