csp信奥赛C++高频考点专项训练之贪心算法 --【线性扫描贪心】:均分纸牌

题目描述
有 N N N 堆纸牌,编号分别为 1 , 2 , ... , N 1,2,\ldots,N 1,2,...,N。每堆上有若干张,但纸牌总数必为 N N N 的倍数。可以在任一堆上取若干张纸牌,然后移动。
移牌规则为:在编号为 1 1 1 堆上取的纸牌,只能移到编号为 2 2 2 的堆上;在编号为 N N N 的堆上取的纸牌,只能移到编号为 N − 1 N-1 N−1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
例如 N = 4 N=4 N=4 时, 4 4 4 堆纸牌数分别为 9 , 8 , 17 , 6 9,8,17,6 9,8,17,6。
移动 3 3 3 次可达到目的:
- 从第三堆取 4 4 4 张牌放到第四堆,此时每堆纸牌数分别为 9 , 8 , 13 , 10 9,8,13,10 9,8,13,10。
- 从第三堆取 3 3 3 张牌放到第二堆,此时每堆纸牌数分别为 9 , 11 , 10 , 10 9,11,10,10 9,11,10,10。
- 从第二堆取 1 1 1 张牌放到第一堆,此时每堆纸牌数分别为 10 , 10 , 10 , 10 10,10,10,10 10,10,10,10。
输入格式
第一行共一个整数 N N N,表示纸牌堆数。
第二行共 N N N 个整数 A 1 , A 2 , ... , A N A_1,A_2,\ldots,A_N A1,A2,...,AN,表示每堆纸牌初始时的纸牌数。
输出格式
共一行,即所有堆均达到相等时的最少移动次数。
输入输出样例 1
输入 1
4
9 8 17 6
输出 1
3
说明/提示
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 100 1 \le N \le 100 1≤N≤100, 1 ≤ A i ≤ 10000 1 \le A_i \le 10000 1≤Ai≤10000。
分析思路
本题要求通过最少的相邻移动次数,使所有堆的纸牌数相等。
已知纸牌总数是堆数 N 的倍数,因此平均值 avg = 总牌数 / N。
贪心策略:从左到右依次处理每一堆。
- 若当前堆的纸牌数不等于
avg,则必须通过一次移动(与右边相邻堆交换)来调整。 - 具体地,计算当前堆与平均值的差值
d = a[i] - avg,然后将这个差值移动到下一堆(即a[i+1] += d),同时移动次数加 1。 - 这样处理后,当前堆就变成了
avg,而差值的影响传递给了下一堆。 - 继续处理下一堆,直到最后一堆(最后一堆必然已是
avg,无需再移动)。
正确性 :
每次移动只涉及相邻两堆,且保证已处理过的左边部分全部达到平均值,不会因为后续调整而破坏。这种局部最优策略最终得到全局最优解,移动次数即为需要调整的堆数(即 a[i] != avg 的堆数,但不包括最后一堆)。
时间复杂度 O(N),空间复杂度 O(N)。
代码实现
cpp
#include <bits/stdc++.h>
using namespace std;
int n; // 堆数
int a[105]; // 每堆纸牌数,最大 N=100,开稍大一点
int s, avg, ans; // s:总和, avg:平均值, ans:最少移动次数
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
s += a[i]; // 累加总张数
}
avg = s / n; // 计算平均值(题目保证整除)
for (int i = 1; i < n; ++i) { // 只处理到第 n-1 堆
if (a[i] != avg) {
// 当前堆与平均值的差值,移动到下一堆
int d = a[i] - avg;
a[i+1] += d;
ans++; // 发生一次移动
}
}
cout << ans << endl;
return 0;
}
功能分析
输入
- 第一行:整数
N(1 ≤ N ≤ 100) - 第二行:
N个整数,表示每堆初始纸牌数(1 ≤ Ai ≤ 10000)
处理
- 计算总和
s和平均值avg。 - 从左到右扫描前
N-1堆:- 若当前堆不等于平均值,则将其与平均值的差值(可正可负)加到下一堆,并计数一次。
- 输出计数结果。
输出
一个整数,即最少移动次数。
样例验证
输入:
4
9 8 17 6
- 总和 = 40,avg = 10
- i=1: a[1]=9 ≠10 → d=-1 → a[2]=8+(-1)=7, ans=1
- i=2: a[2]=7 ≠10 → d=-3 → a[3]=17+(-3)=14, ans=2
- i=3: a[3]=14 ≠10 → d=4 → a[4]=6+4=10, ans=3
- 输出
3,与题目示例一致。
各种学习资料,助力大家一站式学习和提升!!!
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"########## 一站式掌握信奥赛知识! ##########";
cout<<"############# 冲刺信奥赛拿奖! #############";
cout<<"###### 课程购买后永久学习,不受限制! ######";
return 0;
}
【秘籍汇总】(完整csp信奥赛C++学习资料):
1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):
https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:
https://edu.csdn.net/course/detail/40437 点击跳转

https://edu.csdn.net/course/detail/41081 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:
CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转
CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转
信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转
4、csp信奥赛冲刺一等奖有效刷题题解:
CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转
5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转
· 文末祝福 ·
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}