把每堆纸牌数减去平均值得到偏差,再求偏差的前缀和;每一条前缀和非零的相邻边界至少要搬一次,且各搬一次即可做到,答案就是非零前缀和的个数。
OJ: roj
题目 ID: 1320
难度:普及-
标签:贪心前缀和
创建: 2026-09-30 05:05
更新: 2026-10-05 09:18
题目描述
有 4 / 9 8 17 6,输出 3。
思路
设平均值
参考代码
Python 版:
python
#!/usr/bin/env python3
# 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-09-30 05:05
# update_at: 2026-09-30 05:05
import sys
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
cards = [next(data) for _ in range(n)]
# 每堆的盈余(正数表示多出来,负数表示还缺):目标就是让每一堆盈余归零
diff = [x - sum(cards) // n for x in cards]
# 前缀和 s[i] = 前 i 堆的盈余之和。相邻两堆之间必须流过的牌数恰好是 |s[i]|,
# 所以不管怎么调度,第 i 次"跨 i/i+1 边界的移动"都少不了;总次数 = 非零前缀和个数。
prefix = 0
moves = 0
for i in range(n - 1): # 最后一条边不用管:总盈余为 0,前 n-1 个前缀为 0 即全部为 0
prefix += diff[i]
moves += prefix != 0
print(moves)
if __name__ == "__main__":
solve()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 09:17
* update_at: 2026-10-05 09:17
*/
// main.cpp:均分纸牌,答案等于「偏差前缀和非零」的个数。
#include <iostream>
using namespace std;
const int MAXN = 105;
typedef long long ll;
ll n;
ll a[MAXN]; // a[i] 表示第 i 堆纸牌的初始张数
ll diff[MAXN]; // diff[i] = a[i] - 平均值,表示第 i 堆多出(正)或缺少(负)的张数
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 avg = sum / n; // 纸牌总数保证是 n 的倍数
// 每条相邻边界 i/i+1 上净流过的牌数被两端盈亏唯一确定,恰好等于偏差的前缀和;
// 前缀和不为零就说明这条边界必须搬一次,所以答案就是非零前缀和的个数。
ll prefix = 0;
ll moves = 0;
for (ll i = 1; i <= n - 1; i++) {
diff[i] = a[i] - avg;
prefix += diff[i];
if (prefix != 0) {
moves++;
}
}
cout << moves << endl;
return 0;
}