题目描述
有NN堆纸牌,编号分别为 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堆纸牌,每堆纸牌初始数,1≤Ai≤10000)
输出格式
一行:即所有堆均达到相等时的最少移动次数。
输入输出样例
输入 #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; }