Is this the way the dynamic programming version of maximum subarray sum algorithm works? - c++

At the dynamic programming chapter in my algorithms textbook I have an example of how to solve the maximum sub array sum problem using this technique. I am not sure if I got the idea behind the algorithm so I will describe here how I think it works (after reading several times about it and doing several examples).
Basically, you have an array A of size n, and you want to find the maximum sub array sum of that array. The sub array with maximum sum can be somewhere in the right half of the array, left half, or somewhere in the middle. So you recursively call the function to compute the maximum sub array sum from the left and, then, from the right side of the array. Then, you compute the maximum sub array sum that from the middle of the array to the end, then compute the maximum sub array sum from the middle to the beginning of the array (it's length is not necessarily n/2). Then, if the sum of maximum sub array sum form left plus maximum sub array sum from the right is bigger than the maximum sub array sum from the left half (the one computed recursively ) and the maximum sub array sum from the right half (also computed recursively), then the maximum sub array sum is in the one in middle. Otherwise is the maximum of the one from left half and the one from right half (those were computed recursively).
Did I got the working mechanism of the algorithm?
This is the function that I was analyzing:
int maxSubArraySum(int* arr, int n)
{
if(n == 1)
{
return arr[0];
}
int m = n / 2;
int left = maxSubArraySum(arr, m);
int right = maxSubArraySum(arr + m, n - m);
int leftsum = INT_MIN, rightsum = INT_MIN, sum = 0;
for(int i = m; i < n; i++)
{
sum += arr[i];
rightsum = std::max(rightsum, sum);
}
sum = 0;
for(int i = (m - 1); i >= 0; i--)
{
sum += arr[i];
leftsum = std::max(leftsum, sum);
}
int retval = std::max(left, right);
return std::max(retval, leftsum + rightsum);
}

One does not need always Recursion to achieve dynamic programming. The Kadane's algorithm is a simple example of dynamic programming by breaking down the problem into subproblems reused n-1 times (compare the last so far maximum sub array to the current one n-1 times).

Related

Smallest kth for quick select algor

I've been working on a program that is supposed to test the performance of quick select algorithm under different group size setting. You find the pivot, the algorithm will divide all the elements into group of 5. Its supposed to find the median of each group and use the median of medians from all group as pivot. I'm having an issue with the smallest kth part. The errors that I'm getting is that n is not a constant variable so it cannot allocate the array and that it causes median to have an unknown size. What should I do to correct this?
int smallestKth(int ray[], int l, int r, int k)
{
if (k > 0 && k <= r - l + 1)
{
int n = r-l+1;
int i, median[(n+4)/5];
for (i=0; i<n/5; i++)
median[i] = medianFind(ray+l+i*5, 5);
if (i*5 < n)
{
median[i] = medianFind(ray+l+i*5, n%5);
i++;
}
int medOfMed = (i == 1)? median[i-1]:
smallestKth(median, 0, i-1, i/2);
int pivotPosition = part(ray, l, r, medOfMed);
if (pivotPosition-l == k-1)
return ray[pivotPosition];
if (pivotPosition-l > k-1)
return smallestKth(ray, l, pivotPosition-1, k);
return smallestKth(ray, pivotPosition+1, r, k-pivotPosition+l-1);
}
return INT_MAX;
}
int median[(n+4)/5]; is a nonstandard declaration supported by some compilers as an extension. Rather than using a Variable Length Array (VLA), you should use std::vector.
std::vector median((n+4)/5);
You don't need to make a new array to hold the medians. Just use a fifth of the original array.
One way to do that is to stride the array; represent the array as a starting pointer, a number of elements, and a stride, which is the distance between two consecutive elements. For example, once you've finished putting tbe median of each group of five in the right place in the array [start, n, stride], you can recurse on the array [start+2, (n+2)/5, 5*stride].
This was solved by creating a pointer for the median array.
int n = right-left+1;
int *median = new int[(n+4)/5];

Counting number of longest increasing subsequences by evolving recursive solution

How can I count the number of longest increasing LIS by evolving my recursive solution for example [1,3,5,4,7] returns 2 where the LIS is 1,3,5,7 and 1,3,4,7similarly for [3,3,3,3] it will be 4 where LIS is 3 and there are 4 of them
I compute LIS recursively as follows: (I can optimize this using memoisation and go further to DP and then to a segmented tree as per various solutions but I would like to intuitively lead myself to them)
int numberOfLis(vector<int>& nums)
{
//Set the size of count to the size of num, since there cannot be an LIS greater than the size of nums
vector<int> count(nums.size(), 0);
//Get the size of the maximum LIS and update the frequency of how many similar sizes have been encountered in the count array
int maxcount = LIS(nums, INT32_MIN, 0, count);
//Return the number of occurances by looking it up in our count.
return count[maxcount];
}
int LIS(vector<int>& nums, int prev, int index, vector<int>& count)
{
if (index == nums.size()) return 0;
int with = 0;
//Increasing sequence, lets select it.
if (nums[index] > prev) with = 1 + helper(nums, nums[index], index + 1, count);
//See if we can do better without the current number
int without = helper(nums, prev, index + 1, count);
//Get the maximum seen so far and update the frequency in count array
int maxcount = max(with, without);
++count[maxcount];
return maxcount;
}
I used a count array vector<int>(nums.size(), 0) to increment the max value as I encounter it as ++count[max(with,without)] where the count of the returned max value would be the answer. This lead the count array to have 4 a count of 1 not 2 which is wrong. I am looking for a way to move forward from here.
Updated: Added code for the count array and added comments
The count for a subsequence is more than an increment, as there can be multiple subsequences that end up with the same length.
Working with your example data, when index is 1, both with and without are 3. count[3] is only incremented once, though, even though there are two subsequences with this length, and 3 is returned as the maximum length. When this is used by the previous call (when index is 0), with will be 4 and without 3. count[4] is only increased by 1, even though there are two subsequences of length 4.
You need to change helper to return not just the length of the longest subsequence, but the number of subsequences that have that length.
First, calculate the longest increasing subsequence length starting at kth element of the array.
Then, using this data use something like:
int numberoflis(int k){
if(LIS(k)==1) return 1;
int ret = 0;
for(int i=k+1; i<N; ++i){
if(A[i] > A[k] && LIS(i) == LIS(k)-1){
ret += numberoflis(i);
}
}
return ret;
}
Now you have number of longest increasing subsequences starting at point k. Use a simple loop to figure out the total number of LISs. Also, you should memoize this - but it is easy.

minimum total move to balance array if we can increase/decrease a specific array element by 1

It is leetcode 462.
I have one algorithm but it failed some tests while passing others.
I tried to think through but not sure what is the corner case that i overlooked.
We have one array of N elements. One move is defined as increasing OR decreasing one single element of the array by 1. We are trying to find the minimum number of moves to make all elements equal.
My idea is:
1. find the average
2. find the element closest to the average
3. sum together the difference between each element and the element closest to the average.
What am i missing? Please provide one counter example.
class Solution {
public:
int minMoves2(vector<int>& nums) {
int sum=0;
for(int i=0;i<nums.size();i++){
sum += nums[i];
}
double avg = (double) sum / nums.size();
int min = nums[0];
int index =0 ;
for(int i=0;i<nums.size();i++){
if(abs(nums[i]-avg) <= abs(min - avg)){
min = nums[i];
index = i;
}
}
sum=0;
for(int i=0;i<nums.size();i++){
sum += abs(min - nums[i]);
}
return sum;
}
};
Suppose the array is [1, 1, 10, 20, 100]. The average is a bit over 20. So your solution would involving 19 + 19 + 10 + 0 + 80 moves = 128. What if we target 10 instead? Then we have 9 + 9 + 0 + 10 + 90 moves = 118. So this is a counter example.
Suppose you decide to target changing all array elements to some value T. The question is, what's the right value for T? Given some value of T, we could ask if increasing or decreasing T by 1 will improve or worsen our outcome. If we decrease T by 1, then all values greater than T need an extra move, and all those below need one move less. That means that if T is above the median, there are more values below it than above, and so we benefit from decreasing T. We can make the opposite argument if T is less than the median. From this we can conclude that the correct value of T is actually the median itself, which my example demonstreates (strictly speaking, when you have an even sized array, T can be anywhere between the two middle elements).

Please tell me the efficient algorithm of Range Mex Query

I have a question about this problem.
Question
You are given a sequence a[0], a 1],..., a[N-1], and set of range (l[i], r[i]) (0 <= i <= Q - 1).
Calculate mex(a[l[i]], a[l[i] + 1],..., a[r[i] - 1]) for all (l[i], r[i]).
The function mex is minimum excluded value.
Wikipedia Page of mex function
You can assume that N <= 100000, Q <= 100000, and a[i] <= 100000.
O(N * (r[i] - l[i]) log(r[i] - l[i]) ) algorithm is obvious, but it is not efficient.
My Current Approach
#include <bits/stdc++.h>
using namespace std;
int N, Q, a[100009], l, r;
int main() {
cin >> N >> Q;
for(int i = 0; i < N; i++) cin >> a[i];
for(int i = 0; i < Q; i++) {
cin >> l >> r;
set<int> s;
for(int j = l; j < r; j++) s.insert(a[i]);
int ret = 0;
while(s.count(ret)) ret++;
cout << ret << endl;
}
return 0;
}
Please tell me how to solve.
EDIT: O(N^2) is slow. Please tell me more fast algorithm.
Here's an O((Q + N) log N) solution:
Let's iterate over all positions in the array from left to right and store the last occurrences for each value in a segment tree (the segment tree should store the minimum in each node).
After adding the i-th number, we can answer all queries with the right border equal to i.
The answer is the smallest value x such that last[x] < l. We can find by going down the segment tree starting from the root (if the minimum in the left child is smaller than l, we go there. Otherwise, we go to the right child).
That's it.
Here is some pseudocode:
tree = new SegmentTree() // A minimum segment tree with -1 in each position
for i = 0 .. n - 1
tree.put(a[i], i)
for all queries with r = i
ans for this query = tree.findFirstSmaller(l)
The find smaller function goes like this:
int findFirstSmaller(node, value)
if node.isLeaf()
return node.position()
if node.leftChild.minimum < value
return findFirstSmaller(node.leftChild, value)
return findFirstSmaller(node.rightChild)
This solution is rather easy to code (all you need is a point update and the findFisrtSmaller function shown above and I'm sure that it's fast enough for the given constraints.
Let's process both our queries and our elements in a left-to-right manner, something like
for (int i = 0; i < N; ++i) {
// 1. Add a[i] to all internal data structures
// 2. Calculate answers for all queries q such that r[q] == i
}
Here we have O(N) iterations of this loop and we want to do both update of the data structure and query the answer for suffix of currently processed part in o(N) time.
Let's use the array contains[i][j] which has 1 if suffix starting at the position i contains number j and 0 otherwise. Consider also that we have calculated prefix sums for each contains[i] separately. In this case we could answer each particular suffix query in O(log N) time using binary search: we should just find the first zero in the corresponding contains[l[i]] array which is exactly the first position where the partial sum is equal to index, and not to index + 1. Unfortunately, such arrays would take O(N^2) space and need O(N^2) time for each update.
So, we have to optimize. Let's build a 2-dimensional range tree with "sum query" and "assignment" range operations. In such tree we can query sum on any sub-rectangle and assign the same value to all the elements of any sub-rectangle in O(log^2 N) time, which allows us to do the update in O(log^2 N) time and queries in O(log^3 N) time, giving the time complexity O(Nlog^2 N + Qlog^3 N). The space complexity O((N + Q)log^2 N) (and the same time for initialization of the arrays) is achieved using lazy initialization.
UP: Let's revise how the query works in range trees with "sum". For 1-dimensional tree (to not make this answer too long), it's something like this:
class Tree
{
int l, r; // begin and end of the interval represented by this vertex
int sum; // already calculated sum
int overriden; // value of override or special constant
Tree *left, *right; // pointers to children
}
// returns sum of the part of this subtree that lies between from and to
int Tree::get(int from, int to)
{
if (from > r || to < l) // no intersection
{
return 0;
}
if (l <= from && to <= r) // whole subtree lies within the interval
{
return sum;
}
if (overriden != NO_OVERRIDE) // should push override to children
{
left->overriden = right->overriden = overriden;
left->sum = right->sum = (r - l) / 2 * overriden;
overriden = NO_OVERRIDE;
}
return left->get(from, to) + right->get(from, to); // split to 2 queries
}
Given that in our particular case all queries to the tree are prefix sum queries, from is always equal to 0, so, one of the calls to children always return a trivial answer (0 or already computed sum). So, instead of doing O(log N) queries to the 2-dimensional tree in the binary search algorithm, we could implement an ad-hoc procedure for search, very similar to this get query. It should first get the value of the left child (which takes O(1) since it's already calculated), then check if the node we're looking for is to the left (this sum is less than number of leafs in the left subtree) and go to the left or to the right based on this information. This approach will further optimize the query to O(log^2 N) time (since it's one tree operation now), giving the resulting complexity of O((N + Q)log^2 N)) both time and space.
Not sure this solution is fast enough for both Q and N up to 10^5, but it may probably be further optimized.

Kth sum in an array

I have an array with n elements ,i need to calculate all n*n sum of pair of two elements (array[i]+array[j]).All sums are arranged in ascending order.I need to find Kth sum
for example:
array[] = {3,4,5}
all sums: {(3+3),(3+4),(4+3),(3+5),(5+3),(4+4),(4+5),(5+4),(5+5)}
K = 6
I need to find value for Kth sum ( in this case 6th sum is 4+4 ,i will return 8);
Solution might be very optimal
this is my solution; it isn't optimal:
for(i=0;i<n;i++)
fin>>a[i];
qsort(a, n, sizeof(int), int_cmp);
for(i=0;i<n;i++)
for(j=i;j<n;j++)
{
sum[k]=a[i]+a[j];
if(i!=j)
sum[++k]=a[i]+a[j];
k++;
}
qsort(sum, n*n, sizeof(int), int_cmp);
cout<<sum[nrs-1];
I have seen a similar kind of question from google interview question in that they use two sorted array instead of one but the solution works.One optimization which will work in O(klogk) can be given here.
To find the maximum value in such a case it is necessary to have calculated all the values lesser than it,ie let i,j be the maximum values in your case 5,5 to consider 5,5 to be max it is necessary to have evaluated both 4,5and 5,4.that is i-1,j and i,j-1 So a working code will be to use a heap in c++ it is a priority queue. The code is as follows
#include <iostream>
#include <queue>
using namespace std;
for(i=0;i<n;i++)
fin>>a[i];
qsort(a, n, sizeof(int), int_cmp);
std::priority_queue<int > heap;
heap.add(pair(n-1, n-1)); // biggest pair n=array size
// remove max k-1 times
for (int i = 0; i < k - 1; ++i) {
// get max and remove it from the heap
max = heap.pop();
// add next candidates
heap.push(pair(max.i - 1, max.j));
heap.push(pair(max.i, max.j - 1));
}
// get k-th maximum element
max = heap.pop();
maxVal = a[max.i] + a[max.j];
Now this one is optimized upto O(k.logk) there is another one which gives O(k).You can find it here.Kth sum in O(k)