信息学奥赛一本通 1320:【例6.2】均分纸牌(Noip2002) | 1828:【02NOIP提高组】均分纸牌 | 洛谷 P1031 [NOIP2002 提高组] 均分纸牌

【题目链接】

ybt 1320:【例6.2】均分纸牌(Noip2002)
ybt 1828:【02NOIP提高组】均分纸牌
洛谷 P1031 [NOIP2002 提高组] 均分纸牌

【题目考点】

1. 贪心

【解题思路】

解法1:贪心

设数组aa[i]表示第i堆纸牌的数量,一共有n堆纸牌。由于最后要均分纸牌,每堆牌最后的数量为纸牌总数除以n,记每堆纸牌平均数量为ave。
考虑第1堆纸牌,第1堆纸牌的数量只能通过从第2堆拿或分给第2堆来改变,有以下几种情况:

  • 如果第1堆纸牌的数量等于ave,则不移动纸牌,看下一堆。
  • 如果第1堆纸牌的数量大于ave,则需要分给第2堆a[1]-ave张纸牌,第2堆纸牌增加a[1]-ave
  • 如果第1堆纸牌的数量小于ave,则需要从第2堆拿来ave-a[1]张纸牌,第2堆纸牌减少ave-a[1],也可以说成第2堆纸牌增加a[1]-ave,不过增加的是负数。

如果第2堆纸牌数量a[2]小于ave-a[1],那么第2堆在分给第1堆纸牌后,第2堆的纸牌数量为负数,这也没关系,就当它有负数张纸牌。

a[i]为负数,其意义为“还需要从后面牌堆获取a[i]张纸牌来分给前面的牌堆”。实际过程为,会先从第3堆调度足够的纸牌到第2堆,然后再分给第1堆。如果第3堆的纸牌也不够呢?会先从第4堆及以后的纸牌调度给第3堆,依此类推。似乎是要进行多次纸牌移动,但实际是在考虑完所有情况,计算好前面的纸牌堆需要的纸牌数量后,相邻的纸牌堆之间只会移动一次纸牌。

在处理完第1堆后,考虑第2堆纸牌。第1堆已经有ave张纸牌了,第2堆纸牌不用再考虑与第1堆的纸牌交换,只需要考虑与第3堆的纸牌交换。思考过程与上述情况类似。
一般地,在考虑第k堆纸牌时,第k堆纸牌的数量只能通过与第k+1堆纸牌交互才可以发生改变

  • 如果第k堆纸牌的数量等于ave,则不移动纸牌,看下一堆。
  • 如果第k堆纸牌的数量不等于ave,则移动纸牌,需要分给第k+1堆a[k]-ave张纸牌。(a[k]-ave为正数或负数,如果为负数,是指第k+1堆分给第k堆ave-a[k]张纸牌)

该过程不断进行,直到考虑第n-1堆,当第n-1堆也是ave张纸牌时,剩下的纸牌数量一定是ave张,第n堆自然就是ave张纸牌了。

【题解代码】

解法1:贪心
#include<bits/stdc++.h>
using namespace std;
int n, a[101], ct;//a[i]:第i堆纸牌的数量 ct:移牌次数 
int main(){
    int ave, sum = 0;//sum:纸牌总和 ave:平均每堆纸牌的数量 
	cin >> n;
	for(int i = 1; i <= n; i++)
    {
		cin >> a[i];
		sum += a[i];//纸牌总数加和 
	}
	ave = sum / n;//纸牌总数一定是n的倍数,所以一定可以整除 
	for(int i = 1; i <= n-1; i++)
	{
		if(a[i] != ave)//如果第i堆不是ave张 
        {
			a[i+1] = a[i+1] + a[i] - ave;//第i+1堆增加a[i]-ave张,或第i+1堆减少ave-a[i]张。 
			ct++;//移动次数加1 
		}
	}
	cout << ct;
	return 0;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值