P1031 [NOIP2002 提高组] 均分纸牌
方法一:
简单贪心题。
如果每个数相等时的数为sum,考虑一个数不等于sum,最好的情况通过一次转移使它变为sum。
所以按顺序处理,当前数少从后面拿,当前数多向后面扔,中间记录次数即可。
考虑正确性,有人会觉得,如果后面的数不够拿成为了负数,需要从更后面拿,就不止一次转移了。
其实,如果遇到上述情况,可以先做后面的数从更后面拿数的操作,再做当前数拿数操作,这样操作总数不变。
由此可证,只要不等于sum,拿一次就好了。
#include <bits/stdc++.h> using namespace std; int n,a[110],sum,ans; int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum+=a[i]; sum/=n; for(int i=1;i<=n;i++) { if(a[i]>sum) ans++,a[i+1]+=a[i]-sum; else if(a[i]<sum) ans++,a[i+1]-=(sum-a[i]); } printf("%d",ans); return 0; }
方法2:
上一个方法够解决这个问题,但考虑如果要输出方案,上面的写法不好实现。
所以要有一点小改进,使每次移动不会出现移动数大于总数的情况。
因为向后扔的操作一定没有这种问题,我们就先处理向后扔的情况,再向前扔。
#include<iostream> #define MAXN 101 using namespace std; int main() { int n, a[MAXN], sum[MAXN]={0}, d; cin >> n; for(int i=1; i<=n; i++) { cin >> a[i]; sum[i]=sum[i-1]+a[i]; } int ave = sum[n]/n; int count=0; for(int i=1; i<=n; i++) { if(sum[i]>i*ave) //sumstd[i]=i*ave, 向后面均匀一些 { d = sum[i]-i*ave; sum[i] -= d; a[i] -= d;//手模或简单思考可证,a[i]此时一定大于等于d. a[i+1] += d; count++; } } for(int i=n; i>=1; i--) { if(a[i]>ave) //向前面均匀一些 { d = a[i]-ave; a[i] -= d; a[i-1] += d; sum[i-1] += d; count++; } } cout << count; return 0; }
以上代码来自洛谷用户gracelv 。