[NOIP2002]均分纸牌题解

纸牌均衡问题与贪心算法
题目描述
  有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若于张纸牌,然后移动。
  移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
  现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
  例如 N=4,4 堆纸牌数分别为:
  ① 9 ② 8 ③ 17 ④ 6
  移动3次可达到目的:
  从 ③ 取 4 张牌放到 ④ (9 8 13 10) -> 从 ③ 取 3 张牌放到 ②(9 11 10 10)-> 从 ② 取 1 张牌放到①(10 10 10 10)。
输入格式
N(N 堆纸牌,1 <= N <= 100)
A1 A2 … An (N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000)
输出格式
所有堆均达到相等时的最少移动次数。
样例输入 :
4
9 8 17 6
样例输出 :
3

题目分析:
要使每一堆的纸牌数目均相同,那么就要将多的移动到少的上面。那么怎么移动才能使步骤最少呢?这个地方就用到了贪心的思路,从最左端开始进行移动,如果第i堆的数目大于平均数,那么移动数加1,将多出来的移动到下一堆。如果第i堆数目小于平均数,那么移动数加1,用下一堆补充缺少的数目。下一堆可以为负数,这是这题的关键。本题中我们只是改变了移动的次序,而移动的总步数不会发生改变。贪心算法就是用最简单的方式让每一堆去达到它应该达到的值,不要去考虑其他因素,这就是本题的解法,也是贪心算法的精髓!就像在看这题讨论的时候的一句话,贪心要大胆!

AC代码:
#include<iostream>
using namespace std;


int N;
int data[101];
int total=0;
int ave;
int cnt=0;


void div()
{
for(int i=1;i<=N;++i)
{
if(data[i]<ave)
{
int need=ave-data[i]; //计算需求量
cnt++;
data[i+1]=data[i+1]-need; //从下一堆获取需求量
}
if(data[i]>ave)
{
int rich=data[i]-ave; //计算剩余量
cnt++;
data[i+1]=data[i+1]+rich; //将剩余量给到下一堆 
}  
}



int main()
{
cin>>N;
for(int i=1;i<=N;++i)
{
cin>>data[i];
total+=data[i];
}
ave=total/N; //均值
div();
cout<<cnt<<endl; 
return 0;

### NOIP 2002 矩形覆盖问题的 Python 题解 #### 问题描述 给定平面上若干个点,需要用尽可能少的 k×k 的正方形来完全覆盖这些点。每个正方形可以任意放置,但边须平行于坐标轴。 --- #### 思路分析 该问题是典型的贪心算法应用案例。通过逐步选择最优的位置放置正方形,使得每次都能覆盖最多的未被覆盖的点。以下是具体实现思路: 1. **数据预处理** 将所有的点按照横坐标排序,便于后续操作[^2]。 2. **贪心策略** 每次选取当前最左侧未被覆盖的点作为起点,尝试找到一个能覆盖最多点的正方形区域,并标记区域内所有点为已覆盖状态。 3. **复杂度优化** 使用集合或布尔数组记录哪些点已被覆盖,从而减少重复计算的时间开销。 --- #### Python 实现代码 以下是一个基于上述思路的 Python 实现方案: ```python def min_squares(points, k): # 对点按 x 坐标排序 points.sort(key=lambda p: (p[0], p[1])) covered = set() count = 0 while len(covered) < len(points): max_covered_count = 0 best_square_top_left = None # 寻找最佳左上角顶点 for point in points: if point not in covered: top_left_x = point[0] top_left_y = point[1] current_covered_points = [] # 枚举当前正方形内的所有可能点 for candidate_point in points: if ( candidate_point[0] >= top_left_x and candidate_point[0] < top_left_x + k and candidate_point[1] >= top_left_y and candidate_point[1] < top_left_y + k ): current_covered_points.append(candidate_point) if len(current_covered_points) > max_covered_count: max_covered_count = len(current_covered_points) best_square_top_left = (top_left_x, top_left_y) # 更新已覆盖的点集 for point in points: if ( point[0] >= best_square_top_left[0] and point[0] < best_square_top_left[0] + k and point[1] >= best_square_top_left[1] and point[1] < best_square_top_left[1] + k ): covered.add(point) count += 1 return count # 测试用例 if __name__ == "__main__": points = [(1, 1), (2, 2), (3, 3), (6, 6), (7, 7)] k = 3 result = min_squares(points, k) print(result) # 输出最少需要多少个正方形 ``` --- #### 关键点解析 1. **排序的作用** 排序是为了方便从左到右依次考虑每一个点,确保每次都优先处理尚未被覆盖的最左边的点。 2. **贪心的选择依据** 每次都试图最大化单个正方形所能覆盖的点的数量,这是局部最优解的一部分。 3. **时间复杂度** 外层循环次数取决于最终使用的正方形数量,而内层循环则遍历所有点以评估某个候选正方形的效果。因此整体复杂度接近 O(n&sup2;),其中 n 是点的数量。 --- #### 注意事项 - 如果输入规模较大,则需进一步优化算法性能,例如采用更高效的区间查询结构(如线段树)替代双重嵌套循环。 - 当前实现假设所有点均为整数坐标;如果涉及浮点数或其他特殊情况,应调整相应逻辑。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值