博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

均分纸牌(NOIP2002)

Posted on 2010-10-24 23:25  桃子在路上  阅读(5082)  评论(0)    收藏  举报

均分纸牌
[问题描述]
  有 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)
[输 出]:
  输出至屏幕。格式为:
  所有堆均达到相等时的最少移动次数。‘
[输入输出样例]
a.in:
 4
 9 8 17 6

屏慕显示:
 3

分析:
    开始容易想到在所有的牌中找到最多的一堆,然后向小的牌堆上移动,一直到所有的牌都相等,但关键是不知道往哪个方向上移动才能达到移动次数最少。
     设a[i]为第i堆纸牌的张数(0<=i<=n),ave为均分后每堆纸牌的张数,ans为最小移到次数。 
     我们按照由左而右的顺序移动纸牌。若第i堆纸牌的张数a[i]超出平均值,则移动一次(ans+1),将超出部分留给下一堆,既第i+1堆纸牌的张数增加a[i]-ave;若第i堆纸牌的张数a[i]少于平均值,则移动一次(ans+1),由下一堆补充不足部分,既第i+1堆纸牌的张数减少ave-a[i]; 
     问题是,在从第i+1堆中取出纸牌补充第i堆的过程中,可能会出现第i+1堆的纸牌数小于零(a[i+1]-(ave-a[i])<0 )的情况,但由于纸牌的总数是n的倍数,因此后面的堆会补充第i+1堆ave-a[i]-a[i+1]+ ave张纸牌,使其达到均分的要求。 我们在移动过程中,只是改变了移动的顺序,而移动的次数不变,因此此题使用该方法是可行的。
      例如:1  2  27 
      我们从第二堆移出9张到第一堆后,第一堆有10张纸牌,第二堆剩下-7张纸牌,再从第三堆移动17张到第二堆,刚好三堆纸牌数都是10,最后结果是对的,从第二堆移出的牌都可以从第三堆得到。
     此题的原理是贪心,从左到右让每堆牌向平均数靠拢。但负数的牌也可以移动,才是此题的关键。


 1//均分纸牌(NOIP2002)
 2program zhipai;
 3var
 4   a:array[1..1000of longint;
 5   n,i,ave,sum,d,ans:longint;
 6
 7procedure init;
 8begin
 9    readln(n);
10    sum:=0;
11    for i:=1 to n do
12    begin
13        read(a[i]);
14        sum:=sum+a[i];
15    end;
16    ave:=sum div n;
17end;
18
19procedure work;
20begin
21    for i:=1 to n do
22      if a[i]<>ave then
23      begin
24         inc(ans);
25         d:=abs(a[i]-ave);
26         if a[i]<ave then a[i+1]:=a[i+1]-d
27                else a[i+1]:=a[i+1]+d;
28      end;
29end;
30
31begin
32    assign(input,'a.in');  reset(input);
33    assign(output,'a.out'); rewrite(output);
34    init;
35    work;
36    writeln(ans);
37    close(input); close(output);
38end.