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
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)。