NOIP 2002 均分纸牌 解题报告

  好题!非常好的贪心题,引用别人的题解吧:

    开始容易想到在所有的牌中找到最多的一堆,然后向小的牌堆上移动,一直到所有的牌都相等,但关键是不知道往哪个方向上移动才能达到移动次数最少。
     设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,最后结果是对的,从第二堆移出的牌都可以从第三堆得到。
     此题的原理是贪心,从左到右让每堆牌向平均数靠拢。但负数的牌也可以移动,才是此题的关键。


  我的代码如下:

#include <stdio.h>
#include <stdlib.h>
int num[100];
int n, need = 0;

int srch(void)
{
	int i;
	int ans = 0;
	for(i = 0; i < n - 1; i++){
		if(num[i] != need){
			num[i + 1] += num[i] - need;
			ans++;
		}
	}
	return ans;
}

int main(int argc, char **argv)
{
	int i;
	scanf("%d", &n);
	for(i = 0; i < n; i++){
		scanf("%d", &num[i]);
		need += num[i];
	}
	need /= n;
	printf("%d\n", srch());
	return 0;
}
posted @ 2011-07-24 20:33  zqynux  阅读(466)  评论(0编辑  收藏  举报