假设一个数组,我们从索引为0的元素开始。我们希望通过最大长度为K的步长从0索引到数组的最后一个索引。
例如,假设一个数组为[10,2,-10,5,20],K为2,这意味着最大步长为2(我们可以假设K总是可能的,并且小于数组的长度)。 现在,我们从索引0开始,当前的分数是10,然后我们可以升至2或可以升至-10。假设我们从此处转到2,所以总分变为10 + 2 = 12。现在从2开始,我们可以达到-10或5,所以您进入5时得分为12 + 5 = 17。从这里您可以直接转到最后一个索引,因为除此之外别无选择,因此总分是17 + 20 = 37。
对于给定的长度为N和整数K的数组,我们需要找到可以获得的最大分数。 我想到了一个解决方案,通过确定天气是否达到索引i来将其分为子问题,然后递归调用其余数组。但是我从这个问题中感觉到了一些动态编程。
如何解决给定大小为N和整数K的数组。
约束:1 <= N <= 100000和1 <= K <= N
答案 0 :(得分:1)
了解O(n*k)
解决方案。
主函数调用为findMax(A,N,K,0)。
MAX = new Array();
MAX[i] = null. For 0<=i<N
null表示尚未填充特定元素。
procedure findMax(A,N,K,i)
{
if (MAX[i]!=null)
return MAX[i];
else if (i==N-1)
MAX[i]=A[i];
else
{
MAX[i]=A[i]+findMax(A,N,K,i+1);
for (j=2; j<=K&&(i+j)<N; ++j)
if (A[i]+findMax(A,N,K,i+j)>MAX[i])
MAX[i]=A[i]+findMax(A,N,K,i+j);
}
return MAX[i];
}
该问题具有最佳的子结构属性。为了计算最佳解,需要计算所有子问题。因此,乍一看,我想时间复杂度不会低于O(n*k)
。
答案 1 :(得分:1)
尝试以这种方式向后走,可以在 O(n * logk)中实现。
如果数组的大小为1,则最大值为该元素。在i元素中考虑您-您可以选择他或下一个K元素之一->选择一个使最终结果最大化的元素。
考虑以下伪代码:
基于@RandomPerfectHashFunction答案并作一些更改
将Max视为我们的答案数组,将树视为AVL Tree(自平衡二进制搜索树)
findMaxStartingFromIndex(A,N,K,i, Max, Tree)
if Max[i] != null
return Max[i]
max = Tree.Max // log k - just go down all the way to the right
if (i + k > N) // less then k element to end of array
max = max(max,0) // take the maximum only if he positive
Max[i] = A[i] + max
Tree.add(Max[i])
if (i + k < N)
Tree.remove(Max[i+k]) // remove the element from tree because it is out of the rolling-window of k elements
return Max[i]
在主屏幕中:
Init Max array at size N
Init Tree as empty AVL tree
Max[N-1] = A[N-1]
Tree.add(MAX[N-1])
for (i = N-2; i >= 0 ; i--)
findMaxStartingFromIndex(A,N,K,i, Nax, Tree)
完成所有操作后,将在 Max 数组的前k个元素中查找最大值(并非总是选择第一个元素是最佳选择)
在二叉搜索树中添加查找和删除元素为log n->在我们的情况下,树仅容纳k个元素->我们实现了O(n * logk)复杂度
答案 2 :(得分:1)
这可以在O(n) time and memory
基本上:从i = n-1回到0,您必须知道从i + 1到i + k的最佳索引是什么?那么对我来说最好的答案就是跳到[i + 1,i + k]范围内的最佳索引
要获取该信息,您可以创建某种队列(但是您需要能够在c ++中从双方执行弹出操作,您可以使用出队)。 在该队列中,您保留两个信息:(时间,值),其中时间是推入元素的时间,而值是从元素开始获得的最佳总和。
现在,当您进入索引i:第一次弹出直到当前时间(最小命名为t)减去queue.top.time为> k:while( t-que.top.time > k) que.pop
然后,您可以获取que.top.value + array[i]
,这是从索引i中可以获得的最佳值。
最后要做的是更新队列。您创建新元素e = (t, que.top.value + array[i])
并执行que.back(而不是que.top)并执行
while (que.back.value <= e.value) que.pop_back
然后您可以后退
que.push_back(e)
增加t ++
之所以起作用,是因为当您的新元素具有更高的价值时,过去插入到que上的元素最好保留该元素,因为您将可以使用更长的时间。
希望有道理:)
答案 3 :(得分:0)
这可以在O(n)
中完成。我假设您已经熟悉基本的DP算法,该算法在O(nk)
中运行。我们有dp[i] = value[i] + (max(dp[j]) for i - k < j < i)
。 k
复杂性的因素来自于在DP数组中找到最后k
个值中的最小值,我们可以将其优化为O(1)
。
一种优化可能是维护一个包含最后k
个值的二进制搜索树,这将成为一个O(n log k)
解决方案。但是我们可以使用双端队列而不是二进制搜索树来做得更好。
我们维护一个双端队列,其中包含最后k
个元素中的最大值的候选对象。在将当前dp
值放入双端队列的后面之前,如果该值小于或等于当前值,则从后面弹出该值。因为当前值都比后边的值更好(或至少一样好),并且在双端队列中的时间更长,所以后侧的值永远不会是双端队列中的最大值,可以将其丢弃。重复此操作,直到后面的值不再小于或等于当前值为止。
如果索引值小于当前索引值k
,我们可以弹出该值。
我们从后面弹出数字的方式使队列一直在减少,因此最大值在前面。
请注意,即使在主循环的迭代中,从后面弹出值的循环可能运行多达n - 1
次,但总复杂度仍然为O(n)
,因为DP阵列最多弹出一次。
答案 4 :(得分:0)
这可以通过动态规划解决。 dp[i] 表示我们可以从 nums[0] 到 nums[i] 收集的最大分数。过渡是 dp[i] = max(dp[i-1], dp[i-2],...,dp[i-k])+nums[i]。时间复杂度为 O(nk)。