📜  门| Gate IT 2007 |问题3(1)

📅  最后修改于: 2023-12-03 15:12:39.178000             🧑  作者: Mango

门 | Gate IT 2007 | 问题3

简介

这是Gate IT 2007的第三个问题,需要解决的问题是根据给出的数据计算数字最小化的任务,需要编写一个程序来实现。

问题描述

有一个长度为n的序列 $a_{1},a_{2},...,a_{n}$ ,现在你可以选择对其中一个数进行-1操作,或者对其中一个数进行+1操作,每进行一次操作将会得到一个cost,求通过一系列操作,能使所有数字的和最小的最小cost。

输入格式

第一行两个整数n,m,表示序列长度和可操作次数。

第二行n个整数,表示给出的序列。

输出格式

一行一个整数,表示能使数字和最小的最小花费。

样例输入
3 2
1 2 3
样例输出
0
代码示例
# 读入数据
n, m = map(int, input().split())
a = list(map(int, input().split()))

# 对数组从小到大排序,并计算全局平均数
a.sort()
avg = sum(a) / n

# 分别计算高于和低于全局平均数的数字个数
cnt1 = sum(1 for x in a if x > avg)
cnt2 = n - cnt1

# 分别计算将高于和低于全局平均数的数字移动到平均数所需的代价cost
cost1 = sum(abs(x - avg) for x in a)
cost2 = sum(abs(x - avg + 1) for x in a)

# 贪心的考虑,如果要使得所有数字的和最小,那么应该把低于平均数的数字都加1,把高于平均数的数字都减1,
# 这样做可以使得和减少2 * min(cnt1, cnt2)。
# 注意,当m < min(cnt1, cnt2)时,不能全部处理完成,这时就要在剩下的数字中寻找到两个数字使得它们之间的距离为1,将其中一个数字+1,一个数字-1
if m >= min(cnt1, cnt2):
    res = cost1 - cost2
    res -= 2 * min(cnt1, cnt2)

    m -= min(cnt1, cnt2)

    if cnt1 < cnt2:
        res += sum(2 * (a[i] - avg + 1) for i in range(cnt1, n) if m > 0)
    else:
        res += sum(2 * (avg - a[i]) for i in range(cnt2) if m > 0)

    print(res)
else:
    idx1 = sorted(range(n), key=lambda i: a[i])[cnt1 - 1]
    idx2 = sorted(range(n), key=lambda i: -a[i])[cnt2 - 1]

    if a[idx1] - avg < avg - a[idx2]:
        if a[idx1] > avg:
            res = cost1 - abs(a[idx1] - avg)
        else:
            res = cost2 - abs(a[idx1] - avg + 1)

        res -= 2 * (m - 1)

        print(res)
    else:
        if a[idx2] > avg:
            res = cost1 - abs(a[idx2] - avg)
        else:
            res = cost2 - abs(a[idx2] - avg + 1)

        res -= 2 * (m - 1)

        print(res)
思路分析

首先,求出全局平均值,分别统计低于平均值和高于平均值的数字个数,找到一种贪心的策略,分别对低于和高于平均值的数字进行+1/-1操作,使得数字和最小。

接下来,在实现过程中,需要注意以下几点:

  1. 处理完低于/高于平均数的数字后,导致另一类数字个数有所增加,需要重新统计低于/高于平均值的数字的个数。

  2. 如果操作次数足够,那么进行贪心操作就足够了。具体来说,最好的情况是原本所有数字的值都等于全局平均数,那么贪心策略得到的结果就是最优的。

  3. 如果操作次数不足,则需要在剩余的数字中寻找到两个数字使得它们之间的距离为1,将其中一个数字+1,一个数字-1,然后再次按照贪心策略进行操作。