1. 问题描述:
给定两个长度为 n 的整数序列 a1,a2,…,an 和 b1,b2,…,bn。现在要对序列 a 进行恰好 k1 次操作,对序列 b 进行恰好 k2 次操作。每次操作具体流程为选取序列中的一个元素,并将其加 1 或减 1。要求所有操作进行完毕以后,∑i=1n(ai−bi) ^ 2 尽可能小。
输入格式
第一行包含三个整数 n,k1,k2。第二行包含 n 个整数 a1,a2,…,an。第三行包含 n 个整数 b1,b2,…,bn。
输出格式
一个整数,表示所有操作进行完毕以后,∑i=1n(ai−bi) ^ 2 的最小可能值。
数据范围
前六个测试点满足,1 ≤ n ≤ 5。
所有测试点满足,1 ≤ n ≤ 10 ^ 3,0 ≤ k1 + k2 ≤ 10 ^ 3,k1 和 k2 都是非负整数,−10 ^ 6 ≤ ai,bi ≤ 10 ^ 6。
输入样例1:
2 0 0
1 2
2 3
输出样例1:
2
输入样例2:
2 1 0
1 2
2 2
输出样例2:
0
输入样例3:
2 5 7
3 4
14 4
输出样例3:
1
来源:https://www.acwing.com/problem/content/description/3998/
2. 思路分析:
对于这种题目需要一步一步进行分析,因为最终需要使得(ai - bi) ^ 2的和最小,而每一次操作只是对ai - bi的绝对值产生影响,所以对于当前的第i个位置不管是对ai还是对bi操作都是绝对值减1,所以对a操作和对b操作是完全等价的,所以不需要分对谁操作,所以总的操作次数为m = k1 + k2即可,我们可以先求解一下每一个ai与bi差的绝对值,令ai与bi差的绝对值为x0,x1,x2,....xn-1,我们可以计算出每一次xi减1之后能够减少的差值是多少:
因为需要使得最终的总和最小所以使应该要令每一次操作之后减去的值最大,所以相当于是在总共有n个有序序列,这些序列中选择前m个最大值,可以发现这是一个经典的模型---多路归并,一般这种模型考察的都会比较隐晦,需要一步步分析才可以得到最终的模型。我们可以使用d数组来记录每一个ai与bi差的绝对值,每一次找到绝对值最大的位置,如果当前的di大于0那么令其减1,如果最大值已经变为0了那么考虑剩余的操作次数,如果剩余的操作次数为偶数那么我们可以通过对其中某个位置进行加1和减1的操作最终结果为0,如果为奇数那么一定要选择一个位置对其操作,所以最终的结果为1,最终我们还需要将剩余的d[i]的平方累计到答案中(因为数据规模不是特别大所以可以直接枚举计算出所有d[i]的最大值,如果数据规模比较大多路归并一般使用堆来维护)。
3. 代码如下:
class Solution:
# 多路归并
def process(self):
n, k1, k2 = map(int, input().split())
m = k1 + k2
a = list(map(int, input().split()))
b = list(map(int, input().split()))
# d记录a与b差值的绝对值
d = list()
for i in range(n):
d.append(abs(a[i] - b[i]))
res = 0
for i in range(m):
t = 0
# 求解当前绝对值最大的位置t
for j in range(n):
if d[j] > d[t]:
t = j
if d[t] == 0:
# 最大值已经变为0了所以记录对应的答案, 如果为奇数那么答案为1为偶数则答案为0
res = (m - i) % 2
break
# 当前最大值的位置减去1
d[t] -= 1
for i in range(n):
# 计算剩余的结果
res += d[i] * d[i]
return res
if __name__ == '__main__':
print(Solution().process())