提问人:sn- 提问时间:4/3/2021 最后编辑:sn- 更新时间:3/20/2022 访问量:1078
通过重复删除第一个和中间、第一个和最后一个或中间和最后一个元素来清空数组的最小成本
Minimum cost to empty an array by repeatedly removing first and middle, first and last, or middle and last elements
问:
这个问题是在一次采访中问我的,我无法想出最佳解决方案。
问题
给定一个偶数大小的数组,我们可以对数组执行任何操作,直到数组变为空,使总成本最小。
操作:
- 删除第一个和最后一个元素 => 成本将是 GCD(first, last)
- 删除第一个和中间元素 => 成本将是 GCD(第一个,中间)
- 删除中间和最后一个元素 => 成本将是 gcd(middle, last)
注意:
- 在偶数大小的数组中,有两个中间元素。因此,将第二个中间元素视为中间元素。
- gcd(x,y) 代表 x 和 y 之间的最大公约数
例:
输入:[2, 4, 8, 6]
输出: 4
解释:
- 在第一个操作中,删除第一个和中间元素,cost = gcd(2,8) = 2
- 在第二个操作中,删除 4 和 6,cost = gcd(4,6) = 2
- 总成本 = 2+2 = 4
答:
4赞
גלעד ברקן
4/5/2021
#1
无论我们执行操作的顺序如何,都可以用四个指针来描述列表的状态,这些指针准确地描述了列表中剩余的两个连续部分。
这些指针由一组特定的规则相关联,这些规则限制了可以实现的状态。我将从使用四指针组合开始,并以记忆状态进行递归。
我们可以通过观察两个连续部分的长度可以相差 0 或 2 来进一步降低状态。我稍后会尝试更新这个答案。
JavaScript 代码(此处提供 Python 中针对暴力破解的随机测试):
function gcd(x, y){
while(y)
[x, y] = [y, x % y];
return x;
}
function f(A){
const memo = {};
const n = A.length;
const mid = n / 2;
function g(l0, l1, r0, r1){
const key = String([l0, l1, r0, r1]);
if (memo.hasOwnProperty(key))
return memo[key];
const len_l = l1 - l0 + 1;
const len_r = r1 - r0 + 1;
if (len_l + len_r == 2){
if (len_r && len_l)
return gcd(A[l0], A[r0]);
else if (len_l)
return gcd(A[l0], A[l0 + 1]);
else
return gcd(A[r0], A[r0 + 1]);
}
let a = A[l0];
let b = A[r1];
const outer = gcd(a, b) + g(l0 + 1, l1, r0, r1 - 1);
if (len_r >= len_l)
var mid = r0 + (len_l + len_r) / 2 - len_l;
else
var mid = l0 + (len_l + len_r) / 2;
b = A[mid];
const _l1 = l1 - (mid == l1 ? 1 : 0);
const _r0 = r0 + (mid == r0 ? 1 : 0);
const left = gcd(a, b) + g(l0 + 1, _l1, _r0, r1);
a = A[r1];
const right = gcd(b, a) + g(l0, _l1, _r0, r1 - 1);
return memo[key] = Math.min(outer, left, right);
}
return g(0, mid - 1, mid, n - 1);
}
let A = [2, 4, 8, 6];
console.log(JSON.stringify(A));
console.log(f(A));
A = [8,5,21,10,25,9]
console.log(JSON.stringify(A));
console.log(f(A));
const n = 200;
A = [];
for (let i=0; i<n; i++)
A.push(i + 1);
console.log(JSON.stringify(A));
console.log(f(A));
评论
0赞
sn-
4/6/2021
+1 的答案和努力,但我做了一些试运行,例子很少。您的代码返回输入,而它应该返回 3。[8,5,21,10,25,9] => gcd(10,9) = 1,剩余 [8,5,21,25] => gcd(21,25) = 1,剩余 [8,5] => gcd(8,5) = 1。总计 = 3。4
[8,5,21,10,25,9]
0赞
גלעד ברקן
4/7/2021
@Kakashi我想我弄清楚了如何进一步降低状态。关键是这一行,以及我们看到 mid 等于 l1 或 r0 的下一行。这意味着这两个部分的长度可以相差 0 或 2。const _l1 = l1 - (mid == l1 ? 1 : 0);
0赞
גלעד ברקן
4/8/2021
@Kakashi,将搜索空间减少到 O(n^3)。面试官是否提供了解决方案的预期复杂性?
1赞
Anatolii
4/9/2021
@גלעדברקן你是对的。不需要额外的阵列。+1
评论
gcd