环状均分纸牌经典问题,建立糖果流转平衡方程,通过代数变形将环上流动代价转化为数轴上一点到各点的曼哈顿距离之和,利用中位数贪心求得最小值。
OJ: roj
题目 ID: 1432
难度:普及+/提高-
标签:贪心中位数数学
创建: 2026-09-30 09:55
更新: 2026-10-07 11:01
形式化题目
给定一个长度为
正解
思路
本题是经典的「环状均分纸牌」问题。
1. 变量定义与平衡方程
设第
对于第
- 初始拥有
颗; - 给了第
个人 颗; - 从第
个人处得到了 颗。
由此可列出每个人的平衡方程:
每传递一颗糖果的代价为
2. 代数递推与转化
将平衡方程移项变形:
依次展开所有变量,用自由变量
令:
则有:
因此,目标函数可化简为:
3. 几何模型:数轴上的中位数
上式在几何上等价于:在数轴上有
由绝对值函数的性质可知,
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;
}复杂度
- 时间复杂度:计算平均数与前缀和差值数组
耗时 ;排序 耗时 ;累加曼哈顿距离耗时 。总时间复杂度为 。在 时,Python 运行耗时约 秒,完全在时限内。 - 空间复杂度:存储原数组与差值数组
占用 空间。
总结
环状均分纸牌相比普通链状问题的难点在于闭环带来的流动自由度。通过设定相邻传递量的未知数并列出平衡方程,我们利用前缀和消元将