我最近在面试中遇到了这个问题,但我无法为其设计出一个算法。
给定一个未排序的整数数组,我们需要找到将此数组转换为等差数列所需的最小成本。如果更改数组中的任何元素,则需要支付1个单位的成本。此外,元素的值范围在(-inf,inf)之间。
我意识到可以使用动态规划解决这个问题,但是我无法解决方程式。对值有一些限制,但我不记得它们是什么。我只需要高层次的伪代码。
我最近在面试中遇到了这个问题,但我无法为其设计出一个算法。
给定一个未排序的整数数组,我们需要找到将此数组转换为等差数列所需的最小成本。如果更改数组中的任何元素,则需要支付1个单位的成本。此外,元素的值范围在(-inf,inf)之间。
我意识到可以使用动态规划解决这个问题,但是我无法解决方程式。对值有一些限制,但我不记得它们是什么。我只需要高层次的伪代码。
编辑 这里有一个正确的解决方案,虽然简单易懂,但其时间复杂度为O(n^3),不是很高效。
function costAP(arr) {
if(arr.length < 3) { return 0; }
var minCost = arr.length;
for(var i = 0; i < arr.length - 1; i++) {
for(var j = i + 1; j < arr.length; j++) {
var delta = (arr[j] - arr[i]) / (j - i);
var cost = 0;
for(var k = 0; k < arr.length; k++) {
if(k == i) { continue; }
if((arr[k] + delta * (i - k)) != arr[i]) { cost++; }
}
if(cost < minCost) { minCost = cost; }
}
}
return minCost;
}
Louis Ricci 的基本想法是寻找最大的等差数列,但他假设这个等差数列必须出现在单次运行中,实际上这个等差数列的元素可以出现在任何位置的子集中,例如:
1 42 3 69 5 1111 2222 8
只需要进行四个更改:
42 69 1111 2222
1 3 5 8
struct solution {
int len;
int pos;
int step;
};
solution longestArithProg(vector<int> const& v) {
solution best = { -1, 0, 0 };
for (int i = 1; i < v.size(); ++i) {
unordered_map<int, int> bestForStep;
for (int j = 0; j < i; ++j) {
int step = (v[i] - v[j]) / (i - j);
if (step * (i - j) == v[i] - v[j]) {
// This j gives an integer step size: record that j lies on this AP
int len = ++bestForStep[step];
if (len > best.len) {
best.len = len;
best.pos = i;
best.step = step;
}
}
}
}
++best.len; // We never counted the final element in the AP
return best;
}
以上的C++代码使用O(n^2)时间和O(n)空间,因为它循环遍历每一对位置i和j,并为每个执行单个哈希读取和写入操作。为了回答原始问题:
int howManyChangesNeeded(vector<int> const& v) {
return v.size() - longestArithProg(v).len;
}
[1, 2, 10, 3, 5]
。我们可以将该数组写成一系列点的序列。(0,1), (1,2), (2,10), (3,3), (4,5)
其中x值是数组项的索引,y值是数组项的值。现在的问题是找到一条通过该集合中最大可能数量的点的线。将数组转换的成本是不在一条直线上的点的数量,当在一条直线上的点的数量最大化时,这个成本最小化。
这个SO帖子给出了一个相当明确的答案:什么是找到经过大多数点的直线最有效的算法?
思路:对于集合中从左到右的每个点P,找到通过该点和P右侧的最大点数的线。(我们不需要查看P左侧的点,因为它们会在早期迭代中被捕获)。
为了找到P右侧最大数量的共线点,对于每个这样的点Q,计算线段PQ的斜率。在哈希映射中累加不同的斜率。映射到最大命中次数的斜率就是你要找的。
技术问题:您可能不希望使用浮点算术来计算斜率。另一方面,如果您使用有理数,则可能需要计算最大公约数以比较分数(通过比较分子和分母),这将使运行时间乘以log n的因素。相反,您应该通过测试是否ad == bc
来检查有理数a/b
和c/d
的相等性。a = [a_1, a_2, ..., a_n]
,令 diffs = [a_2-a_1, a_3-a_2, ..., a_n-a_(n-1)]
。diffs
中出现最多的值,并将 a
中必要的值调整,以便所有相邻的值之间差异为该值。