均分纸牌

来源:Vijos 1123
均分纸牌
描述
有 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)

输出格式
所有堆均达到相等时的最少移动次数。

样例1
样例输入1
4
9 8 17 6
Copy
样例输出1
3
Copy
限制
每个测试点1s

考点:贪心
这个题直接做看起来并不是很好做,所以考虑先简化问题。首先注意到读入所有数据后可以直接求出平均数,再用每堆牌的初始数减去平均数,可以发现:
(以9 8 17 6为例)
原数组:9 8 17 6
新数组:-1 -2 7 -4
新数组中,每个数的含义变成这堆牌还需要(去掉)几张牌才能达到一样多时需要的牌数。-x代表需要x张牌,+x代表去掉x张牌。所以任务就变成了求最少的移动次数使得所有数都变为0。
思考发现,只用从左向右(或反向)遍历一次即可求出答案。这里贪心的思想是不能有任何多余的移动,就是说对于新数组中的任何数,只要它已经变为0,就不应该再对它进行任何操作。由于题目的限制条件,最左(右)的数只能和它的右(左)边传递牌数,也就是说,如果一开始就让最左(右)的数变为0,则接下来的其他数也将只能和某一边传递牌数,进行同样的操作,就能保证在没有任何多余的移动的情况下将所有数都变为0,而这样的操作仅需一次遍历就能够轻松完成。
例如:-1 -2 7 -4
变为:0 -3 7 -4
变为:0 0 4 -4
变为:0 0 0 0
一共进行了3次移动。
还要注意的地方是,这里给出的样例中出现了负数牌数的传递,这实际上就不是给出牌了,而是从别的牌堆获得牌,尽管有的时候这样的牌堆自身还需要牌,但这不过是操作顺序的问题,不会影响结果。
然后遍历过程中如果遍历到某个数时它本身就是0,就不计数,否则都计数一次,最终计数结果即为答案。
上代码:

#include <iostream>
using namespace std;
const int MAXN = 107;
int n, sum, cnt, A[MAXN];
int main()
{
	cin >> n;
	for (int i = 1; i <= n; ++i)
		cin >> A[i], sum += A[i];
	for (int i = 1; i <= n; ++i)
		A[i] -= (sum / n);
	for (int i = 1; i < n; ++i)
	{
		if (A[i]) ++cnt;
		A[i + 1] += A[i];
	}
	cout << cnt << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值