题干部分:
问题描述
小C 和小U 有两个数组,分别是 a 和 b,它们的长度相同。小U 想通过重新排列数组 a 的元素,来最小化 a 和 b 之间的差异。具体来说,他们要最小化所有元素差值绝对值之和,即 sum(abs(a[i] - b[i]))。
你能帮助小C 和小U 找到这个最小化的值吗?
测试样例
样例1:
输入:
a = [2, 1, 3, 2], b = [5, 2, 4, 2]
输出:5
样例2:
输入:
a = [1, 4, 6], b = [2, 5, 7]
输出:3
样例3:
输入:
a = [1, 9, 6], b = [2, 5, 7]
输出:4
题目分析
若想达到数组差值的最小化,可以优先考虑求数组中每组元素差值绝对值较小值的和。即考虑通过贪心算法对该题目进行求解。由于贪心算法的证明较难,不妨先尝试采取贪心算法的解题策略对该题进行尝试求解。当然,我也会给出自己的证明理解。
题目解答
#include <iostream>
#include <vector>
#include <algorithm>//原题中的string头文件无用,故进行了舍弃
using namespace std;
long long solution(vector<int> a, vector<int> b) {
sort(a.begin(), a.end());
sort(b.begin(), b.end());
long long ans = 0;
for (int i = 0; i < a.size(); ++i) {
ans += abs(a[i] - b[i]);
}
return ans;
}
int main() {
vector<int> a1 = {2, 1, 3, 2}, b1 = {5, 2, 4, 2};
vector<int> a2 = {1, 4, 6}, b2 = {2, 5, 7};
vector<int> a3 = {1, 9, 6}, b3 = {2, 5, 7};
cout << (solution(a1, b1) == 5) << endl;
cout << (solution(a2, b2) == 3) << endl;
cout << (solution(a3, b3) == 4) << endl;
return 0;
}
提交代码,发现解题成功,该题目使用贪心算法求解是有效的。
算法证明
假设 a 和 b 已经排序,并且 a[i] 和 b[i] 匹配。我们需要证明:
其中 j 是任意排列。
即证明,表示在数组 a 和 b 已经排序的情况下,按顺序匹配 a[i] 和 b[i] 的差值总和小于等于任何其他匹配方式的差值总和。
具体证明步骤
-
排序的必要性:
- 假设我们不对数组
a和b进行排序,而是直接计算差值。由于abs(a[i] - b[i])的值取决于a[i]和b[i]的相对大小,如果不排序,可能会出现某些a[i]和b[i]的差值较大的情况,从而导致总差值较大。 - 通过排序,我们可以确保
a[i]和b[i]在相对大小上尽可能接近,从而最小化差值。
- 假设我们不对数组
-
贪心选择性质:
- 假设我们有一个最优解,其中
a和b的元素没有按顺序匹配。我们可以通过交换a和b中的元素,使得a[i]和b[i]的差值更小。 - 具体来说,假设
a和b已经排序,但a[i]和b[j]匹配,其中i != j。我们可以通过交换a[i]和a[j]或b[i]和b[j],使得a[i]和b[i]匹配,从而减少差值。
- 假设我们有一个最优解,其中
-
最优子结构性质:
- 假设我们已经对
a和b进行了排序,并且按顺序匹配它们。对于每个i,a[i]和b[i]的差值是独立的,不会影响其他a[j]和b[j]的差值。 - 因此,通过按顺序匹配
a和b,我们可以确保每个a[i]和b[i]的差值最小,从而使得总差值最小。
- 假设我们已经对
至此,我们成功证明了贪心算法的正确性。
题目总结
当我们在解题过程中遇到一些疑似使用贪心算法求解,且自己对其他解法毫无思路的题目时,不妨使用贪心算法进行尝试,或许你的直觉就是正确的。