I am given an array and asked to find the maximum possible sum of n consecutive numbers, where the maximum sum is less than a given value k.
For example:
array = {1, 3, 1, 2, 3, 4, 1}
k = 7
Here, the answer must be 6, because the max sum that we can obtain that is less than 7 is: arr[1] + arr[2] + arr[3] = 3 + 1 + 2 = 6
How can I write an algorithm to find such a value?
(I have done it with a nested for loop, but it takes too much time, is there any other way to make this program work?)
Basic Foundation
First off, I'd suggest you read up a bit more about time complexity. There are enough good resources out there, and Complexity Theory is one of them. This should help you understand why your solution is not fast enough.
O(n^3) Solution
A brute-force approach is to check all possible subarrays, by iterating over the start and end points of the subarray, and adding up all elements in between.
Given an array arr of size n, the way to do that would be as follows:
for (int l = 0; l < n; ++l) {
for (int r = l; r < n; ++r) {
long sum = 0;
for (int pos = l; pos <= r; ++pos) {
sum += arr[pos];
}
if (sum < k)
max = Math.max(max, sum);
}
}
The final answer is stored in max.
O(n^2) Solution
A faster solution would eliminate the third loop by making use of prefix sums. This can be done as follows, with the help of an auxiliary array preSum of the same size as the main array, where preSum[i] stores the sum of the first i elements:
preSum[0] = arr[0];
for (int i = 1; i < n; ++i)
preSum[i] = preSum[i - 1] + arr[i];
for (int l = 0; l < n; ++l) {
for (int r = l; r < n; ++r) {
long sum = preSum[r];
if (l > 0)
sum -= preSum[l - 1];
if (sum < k)
max = Math.max(max, sum);
}
}
O(n) solution
The most efficient solution to this problem uses a sliding window/two-pointer approach. Note that we assume that negative numbers are not allowed.
We start with both l and r at the beginning of the array. There are two possible cases at every stage:
Sum of the current subarray <k: We can be hopeful and try to add more elements to the subarray. We do this by moving r one step further to the right.
Sum of the current subarray >=k: We need to remove some elements to make the sum satisfy the given constraint. This can be done by moving l one step to the right.
This is repeated till we hit we need to increment r, but have reached the end of the array. The code looks something like this:
long max = 0;
int l = 0;
int r = 0;
long sum = arr[0];
while (true) {
if (sum >= k) {
sum -= arr[l];
++l;
} else {
if (r == n - 1)
break;
else {
++r;
sum += arr[r];
}
}
if (sum < k)
max = Math.max(max, sum);
}
Related
I am trying to determine the total number of comparisons (without counting the loops’ stopping conditions) and how the complexity grows with N.
1.
for (int ind = 0; ind < N; ind++)
{
if (arr[ind] < 0)
arr[ind] = arr[ind] * 2;
}
2.
bool there_are_duplicates = false;
for (int ind1 = 0; ind1 < N; ind1++)
{
for (int ind2 = 0; ind2 < N; ind2++)
if (ind1 != ind2 && arr[ind1] == arr[ind2])
there_are_duplicates = true;
}
3.
for (int ind = 0; ind < N; ind++)
{
int sum = 0;
int nb = 0;
for (int n = 0; n < 4; n++)
{
if (ind >= n)
{
sum = sum + arr[ind - n];
nb++;
}
}
arr[ind] = sum / nb;
}
For the first one, complexity is N and number of comparisons is also N or its just equal to one?
For the second one, complexity is N^2 and number of comparisons is 2(N^2) or its just equal to two?
For the second one, complexity is N and number of comparisons is also 4N or its just equal to one or 4?
Let's count:
You compare for each item of the array, so you have N comparisons and O(N) complexity
You have nested loop, you compare every item (N of them) with all the others but theirselves (i.e. with N - 1 items). You have N * (N - 1) comparisons and O(N^2) complexity
Here, you process each item 4 times. So, you have 4 * N comparisons and O(N) complexity
There is a linear garden from 1 to n. At each point there is a fountain. Given array a[n]tells info about fountain such that its range is max(i-a[i],1) to the left of fountain to min(i+a[i],n) to the right of fountain. Find minimum no. of fountains needed to be activated so that whole garden is covered.
e.g.if n=3 and a={1,2,1} then second fountain has range 1 to 3. So only 1 fountain needed. Here fountain at 1 has range of 1 to 2,
fountain at 2 has range of 1 to 3 and fountain at 3 has range of 2 to 3
So only fountain 2 is enough to be activated to cover the whole garden.
Traverse through all the fountains and construct array jumps:
jumps[i] = {max range of all the fountains having their range's left boundary at i}
for (int i = 1; i <= n; i++){
l = max(1, i - arr[i-1])
r = min(n, i + arr[i-1])
jumps[l] = max(jumps[l], r - l);
}
Then apply Jump Game II (https://leetcode.com/problems/jump-game-ii/) on jumps array.
This takes O(n) time
I think you are looking for this solution off O(n) time and O(n) space.
Create interval array to store the right most range which can be covered from the current index.
public int findMinActivatedFountain(int[] arr, int n) {
int[] interval = new int[n];
int count = 1;
// At each index we'll store the max right possible from this index.
for (int i = 1; i <= n; i++) {
int left = Math.max(i - arr[i - 1], 1);
int right = Math.min(i + arr[i - 1], n);
interval[left - 1] = right;
}
int right = interval[0];
int currMax = right;
for (int i = 1; i < n; i++) {
currMax = Math.max(currMax, interval[i]);
// If the last fountain can cover only this point, then update with next fountain.
if (i == right) {
count++;
right = currMax;
}
}
return count;
}
Ravi's answer is correct, but there's a little bug in that
// At each index we'll store the max right possible from this index.
for (int i = 1; i <= n; i++) {
int left = Math.max(i - arr[i - 1], 1);
int right = Math.min(i + arr[i - 1], n);
interval[left - 1] = right;
}
Here it should be interval[left-1] = max(interval[left-1],right),
We should store the max of all ranges with that point being the starting point. Rest all is correct.
Assign most powerful fountain so that it covers 1 to 2 * range -1
Remove assigned fountain and 1 to 2 * range - 1 from garden
recurse
Time complexity- O(n)
store the lower and upper limits of the range in an array (fountainArray below)
take a stack, to select all the fountains you will need. Initially stack is empty.
rightLimit =0;
leftLimit = 0;
for(i=0; i<n; i++){
if(rightLimit> fountainArray[i].rightLimit){
if(leftLimit<= fountainArray[i].leftLimit){
fountainStack.pop();
//similarly check left limit of the new fountain on top
//keep popping elements till fountainArray[i].leftLimit is > leftLimit of
//fountain on top of stack
}
fountainStack.push(fountainArray[i]);
}
}
return fountainStack.length();
public static int find(int arr[], int n){
for (int i = 1; i <= n; i++){
int l = max(1, i - arr[i-1])
int r = min(n, i + arr[i-1])
jumps[l] = max(jumps[l], r - l);
}
int longest = jump(jumps);
if(arr.lenght == longest){
return 0;
}else{
return arr.length-longest-1;
}
}
// get the minimum jump to reach
public static int jump(int[] A) {
int sc = 0;
int e = 0;
int max = 0;
for(int i=0; i<A.length-1; i++) {
max = Math.max(max, i+A[i]);
if( i == e ) {
sc++;
e = max;
}
}
return sc;
}
The aim of this assignment is to find the number of pairs can be formed by every two number in an array. The condition is that these two number can not have common factors.
I have tried using loop comparing number by number in an array with a loop of factor starts from 2. This code works but it exceeds the time limit for 2 out of 10 cases on codecrunch.
double estimate_PI(int list[], int size) {
int i, pair;
pair = size * (size - 1) / 2; //total number of pairs can be formed
int count = pair;
int j, l;
for (i = 0; i < size; i++) { //check the first number in the array
for (j = i + 1; j < size; j++) { //check compare the first number of the rest
// of the numbers in the array
for (l = 2; l < list[j]; l++) { //check for common factors
if (list[i] % l == 0 && list[j] % l == 0) { //if these two values have common factor
count--; //the possible number of pair reduce by 1
break;
}
}
}
}
// printf("%d\n count",count);
double PI = sqrt(6.0000 * pair / count);
return PI;
}
For this method it takes too long for the codecrunch to run and it mark me wrong.
Rather than try every value [2...list[j]), perhaps look for the Greatest common divisor
Example int gcd(int a, int b) Arjun Sreedharan or chux
#if 0
for (l = 2; l < list[j]; l++) { //check for common factors
...
}
#else
if (gcd(list[i], list[j]) <= 1) count--;
#endif
Simplification possible as only the first factor > 1 needs to be found.
Problem:
Given two arrays A and B, both of size n, find the interval [i,j] (0 <= i,j <= n-1) that maximizes the value of V = sum(A[i:j]) - min(B[i:j]).
Without the array B twist, this problem is just the maximum subarray sum problem, solvable in O(N) with Kadane's algorithm. Now, we have a second array, and we select the minimum element from the range, and deduct it from the sum.
Example:
A = [-5, 2, 3, 4, 5]
B = [-5, 1, 2, 0, -5]
Solution: 19
i=1 to j=4
2+3+4+5 - (-5) = 19
A trivial algorithm is to do a double loop to calculate each (i,j) interval, but this naive approach has O(N^2) time complexity.
I have been trying to find an O(N), or at least an O(NlogN) algorithm, but I haven't been able to achieve it yet.
I would appreciate any ideas on this, thanks!
Edit: The implementation of the solution by Peter for reference:
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int kadane_modified(vector<int>& A, vector<int>& B){
if(A.empty() || B.empty()) return 0;
int size = A.size();
// Backward Kadane's
vector<int> R(size);
int max_so_far = INT_MIN, max_starting_here = 0;
for (int i = size-1; i >= 0; i--)
{
max_starting_here = max_starting_here + A[i];
if (max_so_far < max_starting_here)
max_so_far = max_starting_here;
if (max_starting_here < 0)
max_starting_here = 0;
R[i] = max_starting_here;
}
// Forward Kadane's
vector<int> F(size);
max_so_far = INT_MIN; int max_ending_here = 0;
for (int i = 0; i < size; i++)
{
max_ending_here = max_ending_here + A[i];
if (max_so_far < max_ending_here)
max_so_far = max_ending_here;
if (max_ending_here < 0)
max_ending_here = 0;
F[i] = max_ending_here;
}
// DP that combines previous results
vector<int> V(size);
for(int k = 0; k < size; k++){
if(k < size-1 & k > 0)
V[k] = A[k] + R[k+1] - B[k] + F[k-1];
else if(k == 0)
V[k] = A[k] - B[k] + R[k+1];
else if(k == size-1)
V[k] = A[k] - B[k] + F[k-1];
}
// The maximum V is our answer
int solution = INT_MIN;
for(int i = 0; i < size; i++){
if(solution < V[i]) solution = V[i];
}
return solution;
}
int main()
{
vector<int> A = {-5, 2, 3, 4, 5};
vector<int> B = {-5, 1, 2, 0, -5};
int solution = kadane_modified(A, B);
cout << solution << endl;
return 0;
}
Output:
19
Kadane's algorithm computes the maximum sum of A ending at each position (call this F[i]).
You can also run Kadane's algorithm on the reversed array A to find the maximum sum of A starting at each position (call this R[i]).
We can then use these two arrays to compute the maximum subarray sum A[i:j]-B[k] where i<=k<=j for each position k (by simply computing F[k-1] + A[k] + R[k+1] - B[k] for each k).
This has then solved the slightly different problem of "Find interval i:j which satisfies i<=k<=j and that maximizes A[i:j] - B[k]". However, the highest value that this takes will be the same as choosing B[k] to be the minimum of B[i:j], so this is equivalent to your original problem.
The complexity of this approach is O(n).
This is a variation of a Maximum_subarray_problem.
Find contiguous subarray of length at most K, in an array of length N ( 0 <= K <= N )
Eg. given [-13,-1,1,1,2,3,1,1] and K = 2, maximum K-subarray sum is 5
Looking for O(N) solution. The trivial solution is O(N*N), checking range between each pair. I feel it can be improved to O(N).
Let your array be indexed with 1 to n. Let f(i) be the maximum subarray that ends at i and prefixSum(i) be the prefix sum up to (and including) index i. Then we have
f(i) = prefixSum(i) - MIN(j = i - K to i - 1, prefixSum(j))
f(i) can be computed in linear time by using a sliding window minimum data structure. Here's another implementation of the queue, it support enqueue, dequeue and find-max/min. Using that queue as a primitive, the algorithm would look like this in pseudocode:
global_max = -infinity
prefixSum[0] = 0
q = new MinQueue()
for i := 1 to n:
prefixSum[i] = prefixSum[i - 1] + a[i]
if i > 1
q.enqueue(prefixSum[i - 1])
if i - K - 1 >= 1
q.dequeue()
global_max = max(global_max, prefixSum[i] - q.min())
This is Java code that performing the same task in O(n)
public void maxSubSet (int[] array, int k)
{
int startIndex = -1;
int max = 0;
for(int i =0; i<k; i++){ //Assuming k<array.length
max += array[i];
startIndex = 0;
}
for(int i=k; i<array.length; i++){
int sum = array[i] - array[i-k];
if (sum > max){
max = sum;
startIndex = i;
}
}
System.out.println("Max Sum:" + max);
for(int i = startIndex; i<startIndex+k; i++)
System.out.println(i+":"+array[i]);
}