我遇到了一个非常简单的面试问题,但我的解决方案不正确。对此有何帮助? 1)我的解决方案中的任何错误? 2)时间复杂度O(n)的任何好主意?
给定一个int数组A[]
,定义X=A[i]+A[j]+(j-i), j>=i
。找到X
的最大值?
int solution(vector<int> &A){
if(A.empty())
return -1;
long long max_dis=-2000000000, cur_dis;
int size = A.size();
for(int i=0;i<size;i++){
for(int j=i;j<size;j++){
cur_dis=A[j]+A[i]+(j-i);
if(cur_dis > max_dis)
max_dis=cur_dis;
}
}
return max_dis;
}
答案 0 :(得分:6)
关键的见解是,只有在您确定它们是否可以证明可用之前,您才能在O(n)中跟踪可能有用的值。
从best_i = best_j = max_i = 0开始。前两个跟踪要在解决方案中使用的i和j值。下一个将记录具有i的最高贡献因子的索引,即A[i] - i
最高的那个。
让我们为i和j&#34; X i,j &#34;的某些值调用X的值,并从记录我们迄今为止的最佳解决方案开始ala X best = X 0,0
沿阵列增加n ......
只要[n]
的价值提供更好的&#34; i&#34;对A[i] - i
的贡献比max_i更新,更新max_i。
每当使用n
作为&#34; j&#34; index产生X max_i,n 大于X best ,best_i = max_i,best_j = n。
j_random_hacker的评论建议我草拟一个证明,但说实话,我不知道从哪里开始。我会尽力解释 - 如果其他人有更好的解释,请填写....
重述问题:最大X i,j 其中j> = i。鉴于我们可以设置X 0,0 的初始X best ,问题是知道何时更新它以及什么。当我们考虑数组中的连续索引作为j的潜在值时,我们想要为某些i生成X i,j = n (下面讨论)以与X best 进行比较。但是,我有什么用?那么,给定从0到n的任何索引是&lt; = j,如果我们从已经访问过的索引中选择最佳的i值,则j> = i约束是不相关的。我们通过将与i相关的贡献与j相关的贡献 - A[i] - i
分开来计算出最佳的i值 - 所以为了准备考虑我们是否有一个j = n的新的最佳解决方案我们必须我们去的时候也要保持best_i变量。
无论它的价值如何 - 当我在寻找解决方案的时候,我在纸上写下了一些想象中的i和j贡献,我可以看到它涵盖了有趣的案例......其中Ci和Cj是贡献与n分别用作i和j相关,类似于
n 0 1 2 3 4
Ci 4 2 8 3 1
Cj 12 4 3 5 9
你会注意到我没有费心去挑选Ci可能是A [i] - 我在Cj是A [j] + j的时候......我可以看到新兴的解决方案适用于任何公式,这将使得捕获有趣的案例变得更加困难。那么 - 有趣的案例是什么?当n = 2时,Ci值高于我们在之前的元素中看到的任何值,但只考虑那些早期元素的知识,我们还无法看到使用它的方法。这种情况是单一的&#34;伟大&#34;问题的复杂性。需要的是Cj值至少为9,因此Xbest得到改善,恰好在n = 4时出现。如果我们在[3]发现了更好的Ci,那么我们就可以了当然想用它。 best_i跟踪等待足够好的Cj值索引的位置。
答案 1 :(得分:2)
我的评论的更长版本:如何从两端迭代数组,尝试找到最高数字,同时减少距离appripriate端的距离。那会找到正确的索引(因而是正确的X)吗?
#include <vector>
#include <algorithm>
#include <iostream>
#include <random>
#include <climits>
long long brutal(const std::vector<int>& a) {
long long x = LLONG_MIN;
for(int i=0; i < a.size(); i++)
for(int j=i; j < a.size(); j++)
x = std::max(x, (long long)a[i] + a[j] + j-i);
return x;
}
long long smart(const std::vector<int>& a) {
if(a.size() == 0) return LLONG_MIN;
long long x = LLONG_MIN, y = x;
for(int i = 0; i < a.size(); i++)
x = std::max(x, (long long)a[i]-i);
for(int j = 0; j < a.size(); j++)
y = std::max(y, (long long)a[j]+j);
return x + y;
}
int main() {
std::random_device rd;
std::uniform_int_distribution<int> rlen(0, 1000);
std::uniform_int_distribution<int> rnum(INT_MIN,INT_MAX);
std::vector<int> v;
for(int loop = 0; loop < 10000; loop++) {
v.resize(rlen(rd));
for(int i = 0; i < v.size(); i++)
v[i] = rnum(rd);
if(brutal(v) != smart(v)) {
std::cout << "bad" << std::endl;
return -1;
}
}
std::cout << "good" << std::endl;
}
答案 2 :(得分:0)
我会用伪代码写,因为我没有太多时间,但这应该是使用递归的最佳表现方式
compare(array, left, right)
val = array[left] + array[right] + (right - left);
if (right - left) > 1
val1 = compare(array, left, right-1);
val2 = compare(array, left+1, right);
val = Max(Max(val1,val2),val);
end if
return val
而不是简单地打电话
compare(array,0,array.length);
我认为我找到了一个非常快的解决方案,但您需要检查它:
你需要重写你的数组,如下所示
Array[i] = array[i] + (MOD((array.lenght / 2) - i));
然后你只需找到数组的2个最高值并求它们,这应该是你的解决方案,差不多是O(n)
等等,也许我错过了什么......我得检查一下。好的,你从这个新数组得到2个最高值,并保存位置i和j。然后你需要从原始数组计算你的结果。
------------编辑
这应该是我测试过的Tony D(在c#中)建议的方法的实现。
int best_i, best_j, max_i, currentMax;
best_i = 0;
best_j = 0;
max_i = 0;
currentMax = 0;
for (int n = 0; n < array.Count; n++)
{
if (array[n] - n > array[max_i] - max_i) max_i = n;
if (array[n] + array[max_i] - (n - max_i) > currentMax)
{
best_i = max_i;
best_j = n;
currentMax = array[n] + array[max_i] - (n - max_i);
}
}
return currentMax;