题目地址:
https://leetcode.com/problems/equal-sum-arrays-with-minimum-number-of-operations/
给定两个只含 1 ∼ 6 1\sim 6 1∼6的数组 A 1 A_1 A1和 A 2 A_2 A2,允许将任一数组的任一数改为另一个值,改成的值必须也是 1 ∼ 6 1\sim 6 1∼6。问最少改动多少个数就能让两个数组的总和相等。
设 A 1 A_1 A1长度为 l 1 l_1 l1, A 2 A_2 A2长度为 l 2 l_2 l2,不妨设 l 1 ≤ l 2 l_1\le l_2 l1≤l2,那么 A 1 A_1 A1可以变成的总和的范围是 [ l 1 , 6 l 1 ] [l_1,6l_1] [l1,6l1],同理 A 2 A_2 A2可以变成的总和范围是 [ l 2 , 6 l 2 ] [l_2,6l_2] [l2,6l2]。要使得两个总和相等,必须有 6 l 1 ≥ l 2 6l_1\ge l_2 6l1≥l2,否则就无解。接着暴力枚举 [ 6 l 1 , l 2 ] [6l_1,l_2] [6l1,l2]的所有数就可以了。对于某个和 x x x,我们看原来的和 s s s与 x x x之间的关系。如果 s > x s>x s>x,那么我们要将数组中的一部分数变小,由于我们希望改动的数字尽可能小,显然我们要从 6 6 6开始枚举, 6 6 6变成 1 1 1可以”消耗掉“ 5 5 5的差值,并且我们需要 ⌈ s − x 5 ⌉ \lceil\frac{s-x}{5}\rceil ⌈5s−x⌉个 6 6 6去做这样的改动,以此类推。如果 s < x s<x s<x同理。如果 s = x s=x s=x那就不用改了。代码如下:
class Solution {
public:
int minOperations(vector<int>& a1, vector<int>& a2) {
int n = a1.size(), m = a2.size();
if (n > m) return minOperations(a2, a1);
if (m > 6 * n) return -1;
vector<int> c1(7), c2(7);
int s1 = 0, s2 = 0;
for (int x : a1) c1[x]++, s1 += x;
for (int x : a2) c2[x]++, s2 += x;
int res = INT_MAX;
for (int k = m; k <= 6 * n; k++)
res = min(res, calc(s1, c1, k) + calc(s2, c2, k));
return res;
}
int calc(int s, vector<int>& c, int t) {
if (s == t) return 0;
int res = 0;
if (s >= t) {
int d = s - t;
for (int k = 6; k > 1; k--) {
int kk = k - 1;
if (c[k] * kk >= d)
// 注意上取整的写法
return res + (d + kk - 1) / kk;
else
res += c[k], d -= c[k] * kk;
}
} else {
int d = t - s;
for (int k = 1; k < 6; k++) {
int kk = 6 - k;
if (c[k] * kk >= d)
return res += (d + kk - 1) / kk;
else
res += c[k], d -= c[k] * kk;
}
}
return res;
}
};
时间复杂度 O ( 6 max { l A 1 , l A 2 } − min { l A 1 , l A 2 } ) O(6\max\{l_{A_1},l_{A_2}\}-\min\{l_{A_1},l_{A_2}\}) O(6max{lA1,lA2}−min{lA1,lA2}),空间 O ( 1 ) O(1) O(1)。