📅  最后修改于: 2023-12-03 15:12:39.178000             🧑  作者: Mango
这是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,将其中一个数字+1,一个数字-1,然后再次按照贪心策略进行操作。