跳转至

[NOIP2002 提高组] T1均分纸牌

[NOIP2002 提高组] 均分纸牌

标签:贪心、递归、递推

分析:

n个纸堆,分别是a1, a2, a3, ... , an,问最少移动几次,可以让n个纸堆上的牌数一样多。

设 a 表示平均纸牌数,也就是移动的目标状态,都达到 a 就和平了。

      x1         x2         x3         x4
a1 ---> a2 ---> a3 ---> a4 ---> a5

这里xi表示的是移动牌数的相对个数可能是正的也可能是负的
正的就是从a1移动到a2负的就是从a2移动到a1

有了这个分析过程我们可以依次的移动牌数得到下面的方程

image-20240528165018435

如何构造这个移动的顺序

如果是 0 0 3 这里不能从左开始移动会出现移动出负数的情况
如果是 0 0 0 3 2最佳移动方案也并不是从最多的3开始分的而是从2开始分的

用归纳的方法构造这个分牌的顺序
      x1
a1 ---> [a2 a3 ... an]
假设[a2...an]是已经均分好的从a1分牌到a2分的牌x1有三种情况
x1 = 0不用移动
x1 > 0从a1移动牌给a2
x1 < 0先操作[a2...an]是他们先合法化让a2保持可以给出去牌的状态然后再把牌分给a1

以上就用归纳法构造出来了分牌的顺序

在具体求解的过程中,利用上面的方程,递推求解,当 xi 非零的时候,就需要发生一次移动牌的行为

#include <bits/stdc++.h>

using namespace std;

const int N = 110;

int a[N], n;

int main()
{
    cin >> n;

    int sum = 0;
    for (int i = 1; i <= n; i++){
        cin >> a[i];
        sum += a[i];
    }

    int ave = sum / n;
    int cnt = 0, x = 0;
    for (int i = 1; i < n; i++){
        x = a[i] - ave + x;
        if (x) cnt++;
    }

    cout << cnt << '\n';

    return 0;
}