3

My friend got asked a question in an interview:

Given an array, say: [4, 6, -10, -1, 10, -20] and a value k, say: 4, you have to return the maximum sum of k elements from the array such that every time you select an element, you need to discard either all elements to its left or those to its right. For e.g., if you select -10, you have to discard either [4,6] or [-1, 10, -20]. For the given e.g., output should be 19 (4 + 6+ -1 + 10).
Note that: number of array elements n <= 10^5; and k<=n.

The way I thought about this problem is to do a two way pass over the array (left to right and then vice versa). While doing a pass, we use a sliding window (multiset) of size k (popping least elements from it if size > k) and keeping track of the maximum sum of the window elements as we slide. Then we reverse the array and repeat the same logic on the reversed array and then return the final max sum:

#include<bits/stdc++.h>
using namespace std;
 
long helper(vector<int>&nums, int k) {
    multiset<long> st;
 
    long sum=0l, res=LONG_MIN;
    for(int j=0; j<nums.size(); j++) {
        sum+=nums[j];
        st.insert(nums[j]);
        if(j<k-1) continue;
        while(st.size()>k) {
            sum-=*st.begin();
            st.erase(st.begin());
        }
        res=max(res, sum);
    }
 
    return res;
}
 
long solution(vector<int>&nums, int k){
    long res1=helper(nums, k);
 
    reverse(begin(nums), end(nums));
    long res2=helper(nums, k);
 
    return max(res1, res2);
}
 
int main() {
    vector<int> nums={-5,4,-10,-1,-5,8,-3};
    cout<<solution(nums, 3)<<endl;
 
    nums={4,4,4,4,4};
    cout<<solution(nums, 4)<<endl;
 
    return 0;
}

I tried it on a few examples and it returns the correct expected responses (ideone.com link). Is my approach correct? Thanks!

Explanation:

a. For [4, 6, -10, -1, 10, -20], we first select 10 (and discard -20 to the rigth).
b. Next we select 4 and discard everything to its left (nothing);Now, we have [6, -10, -1]
c. Next we select 6 and discard everything to its left (nothing);Now we have [-10, -1]
d. We finally select -1.

Someone
  • 611
  • 4
  • 13
  • 4
    I don't understand how the constraint of discarding elements makes any difference in the resulting maximum sum. Just pick the elements from left to right, discarding the left part each time. – Nelfeal Nov 18 '22 at 17:29
  • @Nelfeal, yes, in my left->right pass, I only add elements to the left of current `i` (equivalently discarding those to the right) and then do vice versa by reversing the array. – Someone Nov 18 '22 at 17:32
  • 2
    So the problem reduces to, "find the `k` largest values in the array". The O(n) way to do that is with a [quickselect](https://en.wikipedia.org/wiki/Quickselect). – user3386109 Nov 18 '22 at 17:32
  • The explanation says you need to discard either all the elements to left or to the right of a chosen element, yet the example result includes elements to both the left and right of the specified element. The explanation doesn't seem to make sense. – Jerry Coffin Nov 18 '22 at 17:34
  • @user3386109 yes, but with a restriction: k-1 values must be on the same side relative to the k-th value. so it ends up being a problem of finding the top k-1 values in 2 (times N, probably) sub arrays. – ichramm Nov 18 '22 at 17:34
  • @JerryCoffin let me add some more explanation. – Someone Nov 18 '22 at 17:35
  • @ichramm You find the `k` largest elements, and then just list them in the same order as they appear in the array. – user3386109 Nov 18 '22 at 17:36
  • ` if you select -10, you have to discard either [4,6] or [-1, 10, -20]` why then the solution includes 4, 6, -1 and 10? – ichramm Nov 18 '22 at 17:36
  • 1
    @user3386109 that would make sense. And then, once I have them ordered I would "select" the left or right most (it doesn't matter which one), and the restriction is met. nice! – ichramm Nov 18 '22 at 17:38
  • @ichramm Yup, that's the idea. The "discard part of the array" requirement really doesn't add anything to the problem (except maybe a little bookkeeping). – user3386109 Nov 18 '22 at 17:40
  • 1
    It doesn't even add bookkeeping. The problem says "you have to return the maximum sum of k elements". That's a number. Nothing else. It doesn't matter how you got it. – Nelfeal Nov 18 '22 at 17:41
  • @Nelfeal Ah, good point. The examples show the chosen elements, but that's not required by the problem statement. So you're right, no bookkeeping required. Just select the `k` largest elements and compute their sum. – user3386109 Nov 18 '22 at 17:43
  • @user3386109 Maybe post that as an answer so that the question status becomes "answered"? – user202729 Nov 18 '22 at 20:07
  • @user3386109 wouldn't Quickselect be `O(n^2)` in the worst case when all the array elements are equal and when `k` is almost (or exactly) equal to `arr.size()`? I think my current solution is `O(NlogK)` in all cases. – Someone Nov 18 '22 at 23:55
  • @Someone Yes, just like quicksort, quickselect has good average performance, and bad worst case performance. So you have to decide whether those corner cases are a real concern. If you want guaranteed O(NlogK) performance, then simply running the array elements through a min-heap will give you that. – user3386109 Nov 19 '22 at 00:08

0 Answers0