NOIP 2002 均分纸牌

题目描述

N堆纸牌,编号分别为 1,2,…,N。每堆上有若干张,但纸牌总数必为N的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为1堆上取的纸牌,只能移到编号为2的堆上;在编号为N的堆上取的纸牌,只能移到编号为N-1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

例如N=44堆纸牌数分别为:

98176

移动3次可达到目的:

从 ③ 取4张牌放到 ④ (9,8,13,10)-> 从 ③ 取3张牌放到 ②(9,11,10,10)-> 从 ② 取1张牌放到①(10,10,10,10)。

输入格式

两行

第一行为:NN堆纸牌,1N100)

第二行为:A1,A2,,An (N堆纸牌,每堆纸牌初始数,1Ai10000)

输出格式

一行:即所有堆均达到相等时的最少移动次数。

输入输出样例

输入 #1
4
9 8 17 6
输出 #1
3

分析:

由题意即可知,贪心策略是我们从头到尾一堆一堆的移(因为我们只能移动到相邻的一堆,所以移动的顺序是肯定不会有影响),每一次移动纸牌,都将一堆纸牌变成我们要的数目(即平均数)
通过样例,我们来粗略分析一下这个策略。
先开一个 p[]数组,p[i]表示第i堆几张纸牌,dis[] 数组(dis是disparity的缩写,意思是 差距,差异....) dis[i]表示第i堆纸牌和目标数的差距,ans记录次数。
(平均数=10)
未移动前:p[1]=9, p[2]=8, p[3]=17, p[4]=6;
dis[1]= -1, dis[2]=-2, dis[3]=7, dis[4]=-4;
ans=0
接下来就是for循环,如果 dis[i]!=0, 就将 p[i+1]+=dis[i] , p[i]-=dis[i] , dis[i+1]+=dis[i] ,ans++ ,否则就跳过
(如果dis[i]<0,即将右边一堆的abs(dis[堆号])张纸牌移动到左边一堆,根据题意这完全成立)
1,
p[1]=10, p[2]=7, p[3]=17, p[4]=6;
dis[1]= 0, dis[2]=-3, dis[3]=7, dis[4]=-4;
ans=1
      2,   p[1]=10,      p[2]=10,     p[3]=14,  p[4]=6; 
dis[1]= 0, dis[2]=0, dis[3]=4, dis[4]=-4;
ans=2
      3,   p[1]=10,      p[2]=10,     p[3]=10,  p[4]=10; 
dis[1]= 0, dis[2]=0, dis[3]=0, dis[4]=0;
ans=3
以下是代码
#include <iostream>
#include <cstdio>

using namespace std;
int main()
{
    int n,p[10001],dis[10001],arv=0,ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&p[i]),arv+=p[i];
    arv/=n;
    for(int i=1;i<=n;i++) dis[i]=p[i]-arv;   //其实你也可以将dis[]和p[]数组合并,只用一个数组去分别表示牌数和差距.
    for(int i=1;i<=n;i++) {if(dis[i]==0) continue;dis[i+1]+=dis[i],ans++;}
    printf("%d",ans);
    return 0;
}
 
  
 
 

猜你喜欢

转载自www.cnblogs.com/majorin/p/11919383.html