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 。
posted @ 2024-07-17 15:05  storms11  阅读(19)  评论(0编辑  收藏  举报