首页 > 代码库 > wikioi 1098 均分纸牌

wikioi 1098 均分纸牌

wikioi 1098 均分纸牌
2002年NOIP全国联赛提高组
题目描述 Description
有 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)。
输入描述 Input Description
第一行N(N 堆纸牌,1 <= N <= 100)
第二行A1 A2 … An (N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000)
输出描述 Output Description
输出至屏幕。格式为:
所有堆均达到相等时的最少移动次数。
样例输入 Sample Input
4
9 8 17 6
样例输出 Sample Output
3

分析:如果你想到把每堆牌的张数减去平均张数,题目就变成移动正数,
加到负数中,使大家都变成0,那就意味着成功了一半!
拿例题来说,平均张数为10,原张数9,8,17,6,变为-1,-2,7,-4,
其中没有为0的数,我们从左边出发:要使第1堆的牌数-1变为0,只须将-1张牌
移到它的右边(第2堆)-2中;结果是-1变为0,-2变为-3,各堆牌张数
变为0,-3,7,-4;
同理:要使第2堆变为0,只需将-3移到它的右边(第3堆)中去,各堆牌
张数变为0,0,4,-4;要使第3堆变为0,只需将第3堆中的4移到
它的右边(第4堆)中去,结果为0,0,0,0,完成任务。
每移动1次牌,步数加1。
也许你要问,负数张牌怎么移,不违反题意吗?
其实从第i堆移动-m张牌到第i+1堆,等价于从第i+1堆移动m张牌到第i堆,
步数是一样的。
如果张数中本来就有为0的,怎么办呢?如0,-1,-5,6,
还是从左算起(从右算起也完全一样),第1堆是0,无需移牌,
余下与上相同;再比如-1,-2,3,10,-4,-6,从左算起,
第1次移动的结果为0,-3,3,10,-4,-6;
第2次移动的结果为0,0,0,10,-4,-6,
现在第3堆已经变为0了,可节省1步,余下继续。

 1 #include <stdio.h>
 2 int main()
 3 {
 4     int n,a[103],i;
 5     long ave=0;
 6     long step;
 7     int j;
 8     
 9     scanf("%d",&n);
10     for(i=0;i<n;i++)
11     {
12         scanf("%d",&a[i]);
13         ave+=a[i];
14     }
15     ave=ave/n;
16     for(i=0;i<n;i++)
17     {
18         a[i]-=ave;
19     }
20     i=0;
21     j=n-1;
22     while(i<n&&a[i]==0) i++; //过滤左边的0 
23     while(j>=0&&a[j]==0) j--;//过滤右边的0 
24     
25     step=0;
26     while(i<j)
27     {
28         a[i+1]+=a[i];
29         a[i]=0;
30         step++;
31         i++;
32         while(a[i]==0&&i<j) i++;//过滤移动过程产生的0 
33     }
34     printf("%ld\n",step);
35     return 0;
36 }

 

wikioi 1098 均分纸牌