Maximum Weight Increasing Subsequence - algorithm

In the Longest Increasing Subsequence Problem if we change the length by weight i.e the length of each element Ai is 1 if we change it to Wi
How can we do it in O(NlogN).
For Example
For an array of 8 Elements
Elements 1 2 3 4 1 2 3 4
Weights 10 20 30 40 15 15 15 50
The maximum weight is 110.
I found the LIS solution on wikipedia but I can't modify it to solve this problem.

Still, we use f[i] denotes the max value we can get with a sequence end with E[i].
So generally we have for (int i = 1;i <= n;i++) f[i] = dp(i); and initially f[0] = 0; and E[0] = -INF;
Now we shall calculate f[i] in dp(i) within O(log(N)).
in dp(i), we shall find the max f[j] with E[j] < E[i] for all 0 <= j < i. Here we can maintain a Segment Tree.
So dp(i) = find_max(1,E[i]-1) + W[i](this takes O(log)), and for every f[i] already calculated, update(E[i],f[i]).
So the whole algorithm takes (O(NlogN)).
Tip: If E[i] varies in a very big range, it can be Discretizationed.

Here is pure recursion implementation in swift:
// input is Array of (a,w), where a is element and w is weight
func lisw(input: [(Int, Int)], index:Int = 0, largestA:Int? = nil)->Int{
guard index < input.count else { return 0 }
let (a,w) = input[index]
if a <= largestA {
return lisw(input: input, index: index + 1, largestA: largestA)
}
let without_me = lisw(input: input, index: index + 1, largestA: largestA == nil ? a : largestA)
let with_me = lisw(input: input, index: index + 1, largestA: a) + w
return max(without_me,with_me)
}
Feel free to add memoization ;)

Related

Length of Longest Subarray with all same elements

I have this problem:
You are given an array of integers A and an integer k.
You can decrement elements of A up to k times, with the goal of producing a consecutive subarray whose elements are all equal. Return the length of the longest possible consecutive subarray that you can produce in this way.
For example, if A is [1,7,3,4,6,5] and k is 6, then you can produce [1,7,3,4-1,6-1-1-1,5-1-1] = [1,7,3,3,3,3], so you will return 4.
What is the optimal solution?
The subarray must be made equal to its lowest member since the only allowed operation is reduction (and reducing the lowest member would add unnecessary cost). Given:
a1, a2, a3...an
the cost to reduce is:
sum(a1..an) - n * min(a1..an)
For example,
3, 4, 6, 5
sum = 18
min = 3
cost = 18 - 4 * 3 = 6
One way to reduce the complexity from O(n^2) to a log factor is: for each element as the rightmost (or leftmost) element of the candidate best subarray, binary search the longest length within cost. To do that, we only need the sum, which we can get from a prefix sum in O(1), the length (which we are searching on already), and minimum range query, which is well-studied.
In response to comments below this post, here is a demonstration that the sequence of costs as we extend a subarray from each element as rightmost increases monotonically and can therefore be queried with binary search.
JavaScript code:
function cost(A, i, j){
const n = j - i + 1;
let sum = 0;
let min = Infinity;
for (let k=i; k<=j; k++){
sum += A[k];
min = Math.min(min, A[k]);
}
return sum - n * min;
}
function f(A){
for (let j=0; j<A.length; j++){
const rightmost = A[j];
const sequence = [];
for (let i=j; i>=0; i--)
sequence.push(cost(A, i, j));
console.log(rightmost + ': ' + sequence);
}
}
var A = [1,7,3,1,4,6,5,100,1,4,6,5,3];
f(A);
def cost(a, i, j):
n = j - i
s = 0
m = a[i]
for k in range(i,j):
s += a[k]
m = min(m, a[k])
return s - n * m;
def solve(n,k,a):
m=1
for i in range(n):
for j in range(i,n+1):
if cost(a,i,j)<=k:
x = j - i
if x>m:
m=x
return m
This is my python3 solution as per your specifications.

Good algorithm for a query related problem

Given N pairs, we have to find the count of pairs that contain an element k in their range, i.e :
If a Pairi is defined as (Xi,Yi), then if Pairi contain K in its range, then Xi <= K <= Yi.
Now we are given Q such queries to handle with each query consisting of an integer K.
Input:
The first line contains two space-separated integers N and Q.
Next N lines follow where each line denotes a pair. Each line contains two space-separated integers.
Next Q lines follow where each line denotes an integer K
Output:
We are to output the count of pairs where Xi i<= K <= Yi for each query
Constraints:
1 <= N,Q <= 105
Time limit: 2 s
Example:
Input-
4 2
1 5
2 5
6 10
7 8
7
9
Output-
2
1
Explanation-
First query K=7 holds for (6,10) and (7,8).
Second query K=9 holds for (6,10) only.
Given below is my code in java with complexity O(NQ).
import java.util.*;
class Query
{
public static void main(String args[])
{
Scanner sc = new Scanner(System.in);
int n,q;
n = sc.nextInt();
q = sc.nextInt();
int x[] = new int[n];
int y[] = new int[n];
for(int i=0;i<n;i++)
{
x[i] = sc.nextInt();
y[i] = sc.nextInt();
}
while(q-->0)
{
int k = sc.nextInt();
int count = 0;
for(int i = 0;i<n;i++)
{
if(x[i] <= k && k <= y[i])
count++;
}
System.out.println(count);
}
}
}
Can somebody provide me with an approach that has a better complexity such as O(N + Q log N)? I thought of using segment trees and such but do not if it would work for this problem and how to implement it here.
A complexity O(NlogN + QlogN) can be obtained by performing a preprocessing before the queries themselves.
1st step: Preprocessing
The goal is to determine the number of intervals associated for each limit A[k] of each interval, and to sort these A[k].
This is performed in the following way: for each input interval [X, Y], the corresponding limits X and Y are put in a array, and we count the number of openings and closures for each limit X:
open[X] ++
close[Y] ++
The reason behind is that each value after X is "gaining'" one interval, and each value after Y is "losing" one interval.
Then, after sorting, the number of intervals of a given limit is obtained recursively:
After the limit: W[0] = n_opening[0], W[i] = W[i-1] + n_opening[i] - n_closure[i]
On the limit: WL[0] = n_opening[0], WL[i] = W[i-1] + n_opening[i]
This is better illustrated by an example. For the input intervals [1,5], [2, 5], [6, 10], [7, 8], the V[] values are given by:
open[] 1 1 0 1 1 0 0
close[] 0 0 2 0 0 1 1
---|---|---|---|---|---|---|---
1 2 5 6 7 8 10
And the W[] and WL[] values are provided by
WL[] 1 2 2 1 2 2 1
W[] 1 2 0 1 2 1 0
---|---|---|---|---|---|---|---
1 2 5 6 7 8 10
2nd step: queries
For each query K, we have first to determine the corresponding interval [A[i], A[i+1]]. As the A[i] are sorted, this an be done in log(N). Then:
If K is outside any interval: m[k] = 0
If K is in the interval ]A[i], A[i+1][, i.e. not equal to any limit, then m[k] = W[A[i]]
if K is equal to a limit A[i], then m[k] = WL[a[i]]
In the previous example:
K = 7 -> m(7) = WL[7] = 2
K = 9 -> m(9) = W[8] = 1

minimizing the maximum hundai by replacing it with bmw

we have given two arrays H[ ] and B[ ] both of size n.
H[i] denotes number of hundai car the (i)th person have.
I have 'm' BMW car,which I can replace with hundai.
B[i] contain number of hundai car equivalent to 1 BMW(means for each person the equivalency may differ).
Given:
H[i]%B[i]=0;
The question is to minimize the max(H[i]) by replacing it with BMW(note that we have only m BMW).
O(n) or O(nlogn) solution required.
Ideas revolving around minimizing .. the maximum of .. are approachable using a binary search I've answered a question along the same lines here: https://stackoverflow.com/a/52679263/10291310
For your case, we can modify the algorithm to,
start = 0, end = 10^18 // Or your max `M` limit
while start <= end:
bmw_used = 0 // Number of bmws used till now for this iteration
mid = (start + end) / 2
// Let's see if its possible to lower down all the hyndais such
// that number of each hundai <= mid
for i in range(0,N):
if H[i] > mid:
// Calculate the number of bmws required to bring H[i] <= mid
bmw_required = ceil(1.0 * (H[i] - mid) / B[i])
bmw_used += bmw_required
// After iterating over the Hyndais
if bmw_used > M:
// We're exceeding the number of bmws, hence increase the number of huyndai
start = mid + 1
else:
// We still have some more bmws left, so let's reduce more hyndais
end = mid - 1
return start
The total runtime complexity of the solution is O(N*log(M)). Cheers!
pseudocode
for k = 1 to m
max_i = 0
for i = 1 to n-1
if H[max_i] < H[i] then max_i = i
if H[max_i] >= B[max_i] then H[max_i] -= B[max_i]
else break
The total complexity is O(mn).
If you can ignore m, it will be O(n).
This algorithm may have some unnecessary loops when there are same H values, but it's not a big problem.

Have O(n^2) algorithm for "two-sum", convert to O(n) linear solution [duplicate]

This question already has answers here:
Find a pair of elements from an array whose sum equals a given number
(33 answers)
Closed 5 years ago.
I have an O(n^2) solution to the classic two-sum problem. Where A[1...n] sorted array of positive integers. t is some positive integer.
Need to show that A contains two distinct elements a and b s.t. a+ b = t
Here is my solution so far:
t = a number;
for (i=0; i<A.length; i++)
for each A[j]
if A[i] + A[j] == t
return true
return false
How do I make this a linear solution? O(n) scratching my head trying to figure it out.
Here's an approach I have in mind so far. i will start at the beginning of A, j will start at the end of A. i will increment, j will decrement. So I'll have two counter variables in the for loop, i & j.
There are couple of ways to improve upon that.
You could extend your algorithm, but instead of doing a simple search for every term, you could do a binary search
t = a number
for (i = 0; i < A.length; i++)
j = binarySearch(A, t - A[i], i, A.length - 1)
if (j != null)
return true
return false
Binary search is done by O(log N) steps, since you perform a binary search per every element in the array, the complexity of the whole algorithm would be O(N*log N)
This already is a tremendous improvement upon O(N^2), but you can do better.
Let's take the sum 11 and the array 1, 3, 4, 8, 9 for example.
You can already see that (3,8) satisfy the sum. To find that, imagine having two pointers, once pointing at the beginning of the array (1), we'll call it H and denote it with bold and another one pointing at the end of the array (9), we'll call it T and denote it with emphasis.
1 3 4 8 9
Right now the sum of the two pointers is 1 + 9 = 10.
10 is less than the desired sum (11), there is no way to reach the desired sum by moving the T pointer, so we'll move the H pointer right:
1 3 4 8 9
3 + 9 = 12 which is greater than the desired sum, there is no way to reach the desired sum by moving the H pointer, moving it right will further increase the sum, moving it left bring us to the initital state, so we'll move the T pointer left:
1 3 4 8 9
3 + 8 = 11 <-- this is the desired sum, we're done.
So the rules of the algorithm consist of moving the H pointer left or moving the T pointer right, we're finished when the sum of the two pointer is equal to the desired sum, or H and T crossed (T became less than H).
t = a number
H = 0
T = A.length - 1
S = -1
while H < T && S != t
S = A[H] + A[T]
if S < t
H++
else if S > t
T--
return S == t
It's easy to see that this algorithm runs at O(N) because we traverse each element at most once.
You make 2 new variables that contain index 0 and index n-1, let's call them i and j respectively.
Then, you check the sum of A[i] and A[j] and if the sum is smaller than t, then increment i (the lower index), and if it is bigger then decrement j (the higher index). continue until you either find i and j such that A[i] + A[j] = t so you return true, or j <= i, and you return false.
int i = 0, j = n-1;
while(i < j) {
if(A[i] + A[j] == t)
return true;
if(A[i] + A[j] < t)
i++;
else
j--;
return false;
Given that A[i] is relatively small (maybe less than 10^6), you can create an array B of size 10^6 with each value equal to 0. Then apply the following algorithm:
for i in 1...N:
B[A[i]] += 1
for i in 1...N:
if t - A[i] > 0:
if B[t-A[i]] > 0:
return True
Edit: well, now that we know that the array is sorted, it may be wiser to find another algorithm. I'll leave the answer here since it still applies to a certain class of related problems.

count the sequence that has the max sum in array O(N)

if i want to count the sequence in the arrays that has the max sum, how can I do it when I have a limit of O(n) time complexity ?
For example : {1,2,3,4,-3} the output will be 4 because the sum of 1+2+3+4 is the maximum sum and there are 4 numbers in that sequence
I know how to do it with O(N^2) time complexity but not with O(n) help ? :)
I think you can to iterate like this :
MaxSum = 0;
CurrentSum = 0;
MaxLen = 0;
CurrentLen = 0;
Index = GetFirstPositiveValue();
// This function returns the first Index where Array[Index] > 0
// O(n)
while (Index < Array.Length()) {
// general loop to parse the whole array
while (Array[Index] > 0 && Index < Array.Length()) {
CurrentSum += Array[Index];
CurrentLen++;
Index++
}
// We computed a sum of positive integer, we store the values
// if it is higher than the current max
if (CurrentSum > MaxSum) {
MaxSum = CurrentSum;
MaxLen = CurrentLen;
}
// We reset the current values only if we get to a negative sum
while (Array[Index] < 0 && Index < Array.Length()) {
CurrentSum += Array[Index];
CurrentLen++;
Index++;
}
//We encountered a positive value. We check if we need to reset the current sum
if (CurrentSum < 0) {
CurrentSum = 0;
CurrentLen = 0;
}
}
// At this point, MaxLen is what you want, and we only went through
// the array once in the while loop.
Start on the first positive element. If every element is negative, then just pick the highest and the problem is over, this is a 1 element sequence.
We keep on summing as long as we have positive values, so we have a current max value. When we have a negative, we check if the current max is higher than the stored max. If so, we replace the stored max and sequence length by the new values.
Now, we sum negative numbers. When we find another positive, we have to check something :
If the current sum is positive, then we can still have a max sum with this sequence. If it's negative, then we can throw the current sum away, because the max sum won't contain it :
In {1,-2,3,4}, 3+4 is greater than 1-2+3+4
As long as we haven't been through the entire array, we restart this process. We only reset the sequence when we have a subsequence generating a negative sum, and we store the max values only if we have a greater value.
I think this works as intended, and we only go through the array one or two times. So it's O(n)
I hope that's understandable, I have trouble making my thoughts clear. Executing this algorithm with small examples such as {1,2,3,-4,5} / {1,2,3,-50,5} / {1,2,3,-50,4,5} may help if I'm not clear enough :)
If you know the maximum sum of a subarray at the end of an array of length N, you can trivially calculate it for one of length N+1:
[..., X] has max subsum S
[..., X, Y] has max subsum max(0, S + Y)
since either you include Y or you have an empty subarray (since the subarray is at the end of the list).
You can find all maximum sums for subarrays ending at any position by building this from an empty list:
[] S = 0
[1] S = 1
[1, 2] S = 3
[1, 2, -4] S = 0
[1, 2, -4, 5] S = 5
You then only need to keep track of the maximum and its width. Here is some Python code demonstrating the algorithm.
def ranges(values):
width = cum_sum = 0
for value in values:
cum_sum += value
width += 1
if cum_sum < 0:
width = cum_sum = 0
yield (cum_sum, width)
total, width = max(ranges([-2, 1, 2, 3, -8, 4, -3]))
total, width
#>>> (6, 3)

Resources