Dynamic Programming Exercise - algorithm

I have been struggling through a dynamic programming exercise and I can't seem to get the hold of it. I'll write here the problem and also it's solution stating explicitly what I don't understand.
We are given 2 sequences u1,u2,...,un and d1,d2,...,dm and a matrix of dimensions n x m built of positive integers C=[cij]. A list of k pairs
((ui1, dj1),(ui2,dj2),...,(uik,djk)) is said to be non-intersecting if
i1 < 12 <..< ik and j1 < j2 <...< jk.
The "compatibility of a list" is said to be the compatibility of the sum of the pairs that it is made of, that is Ci1j1 + Ci2j2 + ... + Cikjk
Example :
Consider the matrix C = [Cij], so Cij = squared(i + j). Let i be
i = 1, 2, 3, j = 1, 2, 3, 4 and k = 2. Some lists of 2 non-intersecting pairs are these ((u1, d2),(u3, d3)) with a compatibility of 9 + 36 = 45,
((u2, d2),(u3, d4)), with compatibility 16 + 49 = 65, and ((u1, d1),(u2, d4)), with compatibility of 4 + 36 = 40. Some lists that are not non-intersecting are the following : ((u2, d2),(u3, d1)),((u1, d4),(u3, d3)),((u3, d2),(u2, d3))
Solution:
M(i, j, t) = maximum cost of t non-intersecting pairs taken from ui,...,un and dj,...dm
Recurrence equation :
M(i, j, t) = max {M(i + 1, j + 1, t − 1) + c(i, j), M(i, j + 1, t),M(i + 1, j, t).}
M(i, j, 0) = 0
M(i, j, t) = −∞, if t > min{n − i + 1, m − j + 1}
M(i, j, t) = 0, if i > n or j > m
I don't under the reccurrence very well and why do we assign −∞ to M(i, j, t) when t > min{n − i + 1, m − j + 1} but 0 when i > n or j > m
The solution is M(1, 1, k).

M(i, j, t) = max {M(i + 1, j + 1, t − 1) + c(i, j), M(i, j + 1, t),M(i + 1, j, t).}
= max
{
M(i+1, j+1, t-1) + c(i, j), <- we know the maximum cost of t-1
non-intersecting pairs taken from
i+1,...,n and j+1,...,m to which
we prepend the pair (i, j).
M(i, j+1, t), <- keep it at t elements and don't prepend anything,
and take the one containing elements from
i,...,n and j+1,...,m
M(i+1, j, t) <- same, but take elements from i+1,...,n and j,...,m
}
This covers all cases: either we prepend the current element and increase the length by 1 or we don't increase the length and take the maximum of the possibilities this (lack of) action entails. You might ask "but what about M(i+1,j+1,t)? that's also a valid possibility." It is, but it's covered by the two other cases: M(i+1,j,t) will check M(i+1,j+1,t) and return it if needed. You could add it yourself to the recurrence, it wouldn't be wrong, just redundant.
why do we assign −∞ to M(i, j, t) when t > min{n − i + 1, m − j + 1}
Because you cannot have a solution in that case. At step i, you can only pick n - i + 1 elements from the first sequence (because you already picked up to i). Same for j. If t > min{n - i + 1, m - j + 1}, then you will not be able to pick the needed number of elements from one of the lists, and you mark that with negative infinity.
but 0 when i > n or j > m
This is just to handle out of range errors. I'm not sure why they choose 0, I would choose negative infinity for this as well just for consistency, or just avoid it altogether by putting conditions in the implementation (if i + 1 >= n then ignore this branch, although you'll still need to return 0/-infinity if none of the branches are valid), but it doesn't really matter.
If you return 0 and the answer is negative, then you'll run into problems. Of course, for your problem, due to the way C is built, we cannot have a negative solution (because C contains squares of numbers, which are >= 0 always). So you could go with 0 instead of negative infinity in the first case as well.
Exercise: can you write a similar recurrence, but for which the solution is given by M(n, m, k)? Define it in words first, and then mathematically.

Related

Traversing a matrix using dynamic programming

Say there's a matrix with N rows and M columns.
You start the traversal at the bottom left, and your current points P is 0, and space S which is larger than 0. At each point in the matrix, the coordinate is either empty or contains points. If the points have size X and value V, you can choose to pick up the points or not when you reach a coordinate.
For traversing the matrix, we can only go up by one row and choose from one of the three columns (i.e. (i + 1, j − 1), (i + 1, j), or (i + 1, j + 1))
Picking up the points increases P by V and decreases S by X.
I'm trying to write a dynamic programming algorithm that would traverse this and return the best path resulting in the largest number of points.
I figure the subproblems are:
L(N, j) = Null
L(i, 0) = max(L(i + 1, 0), L(i + 1, 1))
L(i, j) = max(L(i + 1, j − 1), L(i + 1, j), L(i + 1, j + 1))
L(i, M) = max(L(i + 1, j - 1), L(i + 1, j))
Would that work? How would I go about introducing this to an algorithm?
You can do it in two ways :
Recursive call for function which should cover all boundary/edge conditions
For example the function call would look like this :
function L(i,j, p, s) :
if(i<0 or j<0) return -1 #edge case
if(s<0) return -1 #out of space
if(p<0) return 0 #no possible outcome
if (s == 0) return p #the end
else return max{L(i + 1, j − 1, p+v, s-x), L(i + 1, j, p+v, s-x), L(i + 1, j + 1, p+v, s-x)}
Iterative with tracking with (s<0) condition and you can maintain the matrix for max S and P values for each step.

Given 2 arrays of non-negative numbers, find the minimum sum of products

Given two arrays A and B, each containing n non-negative numbers, remove a>0 elements from the end of A and b>0 elements from the end of B. Evaluate the cost of such an operation as X*Y where X is the sum of the a elements removed from A and Y the sum of the b elements removed from B. Keep doing this until both arrays are empty. The goal is to minimize the total cost.
Using dynamic programming and the fact that an optimal strategy will always take exactly one element from either A or B I can find an O(n^3) solution. Now I'm curious to know if there is an even faster solution to this problem?
EDIT: Stealing an example from #recursive in the comments:
A = [1,9,1] and B = [1, 9, 1]. Possible to do with a cost of 20. (1) *
(1 + 9) + (9 + 1) * (1)
Here's O(n^2). Let CostA(i, j) be the min cost of eliminating A[1..i], B[1..j] in such a way that the first removal takes only one element from B. Let CostB(i, j) be the min cost of eliminating A[1..i], B[1..j] in such a way that the first removal takes only one element from A. We have mutually recursive recurrences
CostA(i, j) = A[i] * B[j] + min(CostA(i - 1, j),
CostA(i - 1, j - 1),
CostB(i - 1, j - 1))
CostB(i, j) = A[i] * B[j] + min(CostB(i, j - 1),
CostA(i - 1, j - 1),
CostB(i - 1, j - 1))
with base cases
CostA(0, 0) = 0
CostA(>0, 0) = infinity
CostA(0, >0) = infinity
CostB(0, 0) = 0
CostB(>0, 0) = infinity
CostB(0, >0) = infinity.
The answer is min(CostA(n, n), CostB(n, n)).

Number of sub sequences of a given array that are divisible by n

I have a sequence of numbers say 1,2,4,0 and I have to find number of sequence divisible by 6.
So we will have 0,12,24,120,240 which means answer will be 5,
The problem is that I devised an algorithm which requires O(2^n) time complexity so basically it iterates through all the possibilities which is naive.
Is there some way to decrease the complexity.
Edit1: multiple copy of digit is allowed. for example input can be 1,2,1,4,3
Edit2: digits should be in order such as in above example 42 420 etc are not allowed
code: This code however is not able to take 120 into account
`#include <stdio.h>
#include<string.h>
#define m 1000000007
int main(void) {
int t;
scanf("%d",&t);
while(t--)
{
char arr[100000];
int r=0,count=0,i,j,k;
scanf("%s",&arr);
int a[100000];
for(i=0;i<strlen(arr);i++)
{
a[i]=arr[i]-'0';
}
for(i=0;i<strlen(arr);i++)
{
for(j=i;j<strlen(arr);j++)
{
if(a[i]==0)
{
count++;
goto label;
}
r=a[i]%6;
for(k=j+1;k<strlen(arr);k++)
{
r=(r*10 + a[k])%6;
if(r==0)
count++;
}
}
label:;
r=0;
}
printf("%d\n",count);
}
return 0;
}
You can use dynamic programming.
As usual, when we decide to solve a problem using dynamic programming, we start by turning some input values into parameters, and maybe adding some other parameters.
The obvious candidate for a parameter is the length of the sequence.
Let our sequence be a[1], a[2], ..., a[N].
So, we search for the value f(n) (for n from 0 to N) which is the number of subsequences of a[1], a[2], ..., a[n] which, when read as numbers, are divisible by D=6.
Computing f(n) when we know f(n-1) does not look obvious yet, so we dig into details.
On closer look, the problem we now face is that adding a digit to the end of a number can turn a number divisible by D into a number not divisible by D, and vice versa.
Still, we know exactly how the remainder changes when we add a digit to the end of a number.
If we have a sequence p[1], p[2], ..., p[k] and know r, the remainder of the number p[1] p[2] ... p[k] modulo D, and then add p[k+1] to the sequence, the remainder s of the new number p[1] p[2] ... p[k] p[k+1] modulo D is easy to compute: s = (r * 10 + p[k+1]) mod D.
To take that into account, we can make the remainder modulo D our new parameter.
So, we now search for f(n,r) (for n from 0 to N and r from 0 to D-1) which is the number of subsequences of a[1], a[2], ..., a[n] which, when read as numbers, have the remainder r modulo D.
Now, knowing f(n,0), f(n,1), ..., f(n,D-1), we want to compute f(n+1,0), f(n+1,1), ..., f(n+1,D-1).
For each possible subsequence of a[1], a[2], ..., a[n], when we consider element number n+1, we either add a[n+1] to it, or omit a[n+1] and leave the subsequence unchanged.
This is easier to express by forward dynamic programming rather than a formula:
let f (n + 1, *) = 0
for r = 0, 1, ..., D - 1:
add f (n, r) to f (n + 1, r * 10 + a[n + 1]) // add a[n + 1]
add f (n, r) to f (n + 1, r) // omit a[n + 1]
The resulting f (n + 1, s) (which, depending on s, is a sum of one or more terms) is the number of subsequences of a[1], a[2], ..., a[n], a[n+1] which yield the remainder s modulo D.
The whole solution follows:
let f (0, *) = 0
let f (0, 0) = 1 // there is one empty sequence, and its remainder is 0
for n = 0, 1, ..., N - 1:
let f (n + 1, *) = 0
for r = 0, 1, ..., D - 1:
add f (n, r) to f (n + 1, r * 10 + a[n + 1]) // add a[n + 1]
add f (n, r) to f (n + 1, r) // omit a[n + 1]
answer = f (N, 0) - 1
We subtract one from the answer since an empty subsequence is not considered a number.
The time and memory requirements are O (N * D).
We can lower the memory to O (D) when we note that, at each given moment, we only need to store f (n, *) and f (n + 1, *), so the storage for f can be 2 * D instead of (N + 1) * D.
An illustration with your example sequence:
-------------------------------
a[n] 1 2 4 0
f(n,r) n 0 1 2 3 4
r
-------------------------------
0 1 1 2 3 6
1 0 1 1 1 1
2 0 0 1 2 4
3 0 0 0 0 0
4 0 0 0 2 5
5 0 0 0 0 0
-------------------------------
Exercise: how to get rid of numbers with leading zeroes with this solution?
Will we need another parameter?

Count number of swaps to sort first k-smallest element using a bubble sort like algorithm

Given an array a and integer k. Someone uses following algorithm to get first k smallest elements:
cnt = 0
for i in [1, k]:
for j in [i + 1, n]:
if a[i] > a[j]:
swap(a[i], a[j])
cnt = cnt + 1
The problem is: How to calculate value of cnt (when we get final k-sorted array), i.e. the number of swaps, in O(n log n) or better ?
Or simply put: calculate the number of swaps needed to get first k-smallest number sorted using the above algorithm, in less than O(n log n).
I am thinking about a binary search tree, but I get confused (How array will change when increase i ? How to calculate number of swap for a fixed i ?...).
This is a very good question: it involves Inverse Pairs, Stack and some proof techniques.
Note 1: All index used below are 1-based, instead of traditional 0-based.
Note 2: If you want to see the algorithm directly, please start reading from the bottom.
First we define Inverse Pairs as:
For a[i] and a[j], in which i < j holds, if we have a[i] > a[j], then a[i] and a[j] are called an Inverse Pair.
For example, In the following array:
3 2 1 5 4
a[1] and a[2] is a pair of Inverse Pair, a[2] and a[3] is another pair.
Before we start the analysis, let's define a common language: in the reset of the post, "inverse pair starting from i" means the total number of inverse pairs involving a[i].
For example, for a = {3, 1, 2}, inverse pair starting from 1 is 2, and inverse pair starting from 2 is 0.
Now let's look at some facts:
If we have i < j < k, and a[i] > a[k], a[j] > a[k], swap a[i] and a[j] (if they are an inverse pair) won't affect the total number of inverse pair starting from j;
Total inverse pairs starting from i may change after a swap (e.g. suppose we have a = {5, 3, 4}, before a[1] is swapped with a[2], total number of inverse pair starting from 1 is 2, but after swap, array becomes a = {3, 5, 4}, and the number of inverse pair starting from 1 becomes 1);
Given an array A and 2 numbers, a and b, as the head element of A, if we can form more inverse pair with a than b, we have a > b;
Let's denote the total number of inverse pair starting from i as ip[i], then we have: if k is the min number satisfies ip[i] > ip[i + k], then a[i] > a[i + k] while a[i] < a[i + 1 .. i + k - 1] must be true. In words, if ip[i + k] is the first number smaller than ip[i], a[i + k] is also the first number smaller than a[i];
Proof of point 1:
By definition of inverse pair, for all a[k], k > j that forms inverse pair with a[j], a[k] < a[j] must hold. Since a[i] and a[j] are a pair of inverse and provided that i < j, we have a[i] > a[j]. Therefore, we have a[i] > a[j] > a[k], which indicates the inverse-pair-relationships are not broken.
Proof of point 3:
Leave as empty since quite obvious.
Proof of point 4:
First, it's easy to see that when i < j, a[i] > a[j], we have ip[i] >= ip[j] + 1 > ip[j]. Then, it's inverse-contradict statement is also true, i.e. when i < j, ip[i] <= ip[j], we have a[i] <= a[j].
Now back to the point. Since k is the min number to satisfy ip[i] > ip[i + k], then we have ip[i] <= ip[i + 1 .. i + k - 1], which indicates a[i] <= a[i + 1.. i + k - 1] by the lemma we just proved, which also indicates there's no inverse pairs in the region [i + 1, i + k - 1]. Therefore, ip[i] is the same as the number of inverse pairs starting from i + k, but involving a[i]. Given ip[i + k] < ip[i], we know a[i + k] has less inverse pair than a[i] in the region of [i + k + 1, n], which indicates a[i + k] < a[i] (by Point 3).
You can write down some sequences and try out the 4 facts mentioned above and convince yourself or disprove them :P
Now it's about the algorithm.
A naive implementation will take O(nk) to compute the result, and the worst case will be O(n^2) when k = n.
But how about we make use of the facts above:
First we compute ip[i] using Fenwick Tree (see Note 1 below), which takes O(n log n) to construct and O(n log n) to get all ip[i] calculated.
Next, we need to make use of facts. Since swap of 2 numbers only affect current position's inverse pair number but not values after (point 1 and 2), we don't need to worry about the value change. Also, since the nearest smaller number to the right shares the same index in ip and a, we only need to find the first ip[j] that is smaller than ip[i] in [i + 1, n]. If we denote the number of swaps to get first i element sorted as f[i], we have f[i] = f[j] + 1.
But how to find this "first smaller number" fast? Use stack! Here is a post which asks a highly similar problem: Given an array A,compute B s.t B[i] stores the nearest element to the left of A[i] which is smaller than A[i]
In short, we are able to do this in O(n).
But wait, the post says "to the left" but in our case it's "to the right". The solution is simple: we do backward in our case, then everything the same :D
Therefore, in summary, the total time complexity of the algorithm is O(n log n) + O(n) = O(n log n).
Finally, let's talk with an example (a simplified example of #make_lover's example in the comment):
a = {2, 5, 3, 4, 1, 6}, k = 2
First, let's get the inverse pairs:
ip = {1, 3, 1, 1, 0, 0}
To calculate f[i], we do backward (since we need to use the stack technique):
f[6] = 0, since it's the last one
f[5] = 0, since we could not find any number that is smaller than 0
f[4] = f[5] + 1 = 1, since ip[5] is the first smaller number to the right
f[3] = f[5] + 1 = 1, since ip[5] is the first smaller number to the right
f[2] = f[3] + 1 = 2, since ip[3] is the first smaller number to the right
f[1] = f[5] + 1 = 1, since ip[5] is the first smaller number to the right
Therefore, ans = f[1] + f[2] = 3
Note 1: Using Fenwick Tree (Binary Index Tree) to get inverse pair can be done in O(N log N), here is a post on this topic, please have a look :)
Update
Aug/20/2014: There was a critical error in my previous post (thanks to #make_lover), here is the latest update.

Counting number of points in lower left quadrant?

I am having trouble understanding a solution to an algorithmic problem
In particular, I don't understand how or why this part of the code
s += a[i];
total += query(s);
update(s);
allows you to compute the total number of points in the lower left quadrant of each point.
Could someone please elaborate?
As an analogue for the plane problem, consider this:
For a point (a, b) to lie in the lower left quadrant of (x, y), a <
x & b < y; thus, points of the form (i, P[i]) lie in the lower left quadrant
of (j, P[j]) iff i < j and P[i] < P[j]
When iterating in ascending order, all points that were considered earlier lie on the left compared to the current (i, P[i])
So one only has to locate all P[j]s less that P[i] that have been considered until now
*current point refers to the point in consideration in the current iteration of the for loop that you quoted ie, (i, P[i])
Let's define another array, C[s]:
C[s] = Number of Prefix Sums of array A[1..(i - 1)] that amount to s
So the solution to #3 becomes the sum ... C[-2] + C[-1] + C[0] + C[1] + C[2] ... C[P[i] - 1], ie prefix sum of C[P[i]]
Use the BIT to store the prefix sum of C, thus defining query(s) as:
query(s) = Number of Prefix Sums of array A[1..(i - 1)] that amount to a value < s
Using these definitions, s in the given code gives you the prefix sum up to the current index i (P[i]). total builds the answer, and update simply adds P[i] to the BIT.
We have to repeat this method for all i, hence the for loop.
PS: It uses a data structure called a Binary Indexed Tree (http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binaryIndexedTrees) for operations. If you aren't acquainted with it, I'd recommend that you check the link.
EDIT:
You are given a array S and a value X. You can split S into two disjoint subarrays such that L has all elements of S less than X, and H that has those that are greater than or equal to X.
A: All elements of L are less than all elements of H.
Any subsequence T of S will have some elements of L and some elements of H. Let's say it has p elements of L and q of H. When T is sorted to give T', all p elements of L appear before the q elements of H because of A.
Median being the central value is the value at location m = (p + q)/2
It is intuitive to think that having q >= p implies that the median lies in X, as a proof:
Values in locations [1..p] in T' belong to L. Therefore for the median to be in H, it's position m should be greater than p:
m > p
(p + q)/2 > p
p + q > 2p
q > p
B: q - p > 0
To computer q - p, I replace all elements in T' with -1 if they belong to L ( < X ) and +1 if they belong to H ( >= X)
T looks something like {-1, -1, -1... 1, 1, 1}
It has p times -1 and q times 1. Sum of T' will now give me:
Sum = p * (-1) + q * (1)
C: Sum = q - p
I can use this information to find the value in B.
All subsequences are of the form {A[i], A[i + 2], A[i + 3] ... A[j + 1]} since they are contiguous, To compute sum of A[i] to A[j + 1], I can compute the prefix sum of A[i] with P[i] = A[1] + A[2] + .. A[i - 1]
Sum of subsequence from A[i] to A[j] then can be computed as P[j] - P[i] (j is greater of j and i)
With C and B in mind, we conclude:
Sum = P[j] - P[i] = q - p (q - p > 0)
P[j] - P[i] > 0
P[j] > P[i]
j > i and P[j] > P[i] for each solution that gives you a median >= X
In summary:
Replace all A[i] with -1 if they are less than X and -1 otherwise
Computer prefix sums of A[i]
For each pair (i, P[i]), count pairs which lie to its lower left quadrant.

Resources