0% found this document useful (0 votes)
101 views

Geeks DP

There are three algorithms described: 1. A dynamic programming algorithm to find the nth ugly number by using multiples of 2, 3, and 5 to generate the sequence, with time complexity of O(n). 2. A longest increasing subsequence algorithm that uses dynamic programming to find the length of the longest subsequence in O(n^2) time or O(n log n) time using a more optimized algorithm. 3. A longest common substring algorithm that uses dynamic programming to find the length of the longest common substring between two strings in O(mn) time by building a table to store longest common suffix lengths.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
101 views

Geeks DP

There are three algorithms described: 1. A dynamic programming algorithm to find the nth ugly number by using multiples of 2, 3, and 5 to generate the sequence, with time complexity of O(n). 2. A longest increasing subsequence algorithm that uses dynamic programming to find the length of the longest subsequence in O(n^2) time or O(n log n) time using a more optimized algorithm. 3. A longest common substring algorithm that uses dynamic programming to find the length of the longest common substring between two strings in O(mn) time by building a table to store longest common suffix lengths.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 111

Ugly Numbers

Ugly numbers are numbers whose only prime factors are 2, 3 or 5. The sequence 1, 2, 3, 4, 5, 6, 8, 9, 10, 12,
15, … shows the first 11 ugly numbers. By convention, 1 is included.

Given a number n, the task is to find n’th Ugly number.

1 Declare an array for ugly numbers: ugly[n]


2 Initialize first ugly no: ugly[0] = 1
3 Initialize three array index variables i2, i3, i5 to point to
1st element of the ugly array:
i2 = i3 = i5 =0;
4 Initialize 3 choices for the next ugly no:
next_mulitple_of_2 = ugly[i2]*2;
next_mulitple_of_3 = ugly[i3]*3
next_mulitple_of_5 = ugly[i5]*5;
5 Now go in a loop to fill all ugly numbers till 150:
For (i = 1; i < 150; i++ )
{
/* These small steps are not optimized for good
readability. Will optimize them in C program */
next_ugly_no = Min(next_mulitple_of_2,
next_mulitple_of_3,
next_mulitple_of_5);

ugly[i] = next_ugly_no
if (next_ugly_no == next_mulitple_of_2)
{
i2 = i2 + 1;
next_mulitple_of_2 = ugly[i2]*2;
}
if (next_ugly_no == next_mulitple_of_3)
{
i3 = i3 + 1;
next_mulitple_of_3 = ugly[i3]*3;
}
if (next_ugly_no == next_mulitple_of_5)
{
i5 = i5 + 1;
next_mulitple_of_5 = ugly[i5]*5;
}

}/* end of for loop */


6.return next_ugly_no

Time Complexity: O(n)
Auxiliary Space: O(n)

Maximum size square sub-matrix with all 1s


Given a binary matrix, find out the maximum size square sub-matrix with all 1s.
Algorithm:

Let the given binary matrix be M[R][C]. The idea of the algorithm is to construct an auxiliary size matrix S[][] in
which each entry S[i][j] represents size of the square sub-matrix with all 1s including M[i][j] where M[i][j] is the
rightmost and bottommost entry in sub-matrix.

1) Construct a sum matrix S[R][C] for the given M[R][C].


a) Copy first row and first columns as it is from M[][] to S[][]
b) For other entries, use following expressions to construct S[][]
If M[i][j] is 1 then
S[i][j] = min(S[i][j-1], S[i-1][j], S[i-1][j-1]) + 1
Else /*If M[i][j] is 0*/
S[i][j] = 0
2) Find the maximum entry in S[R][C]
3) Using the value and coordinates of maximum entry in S[i], print
sub-matrix of M[][]

Longest Increasing Subsequence


The Longest Increasing Subsequence (LIS) problem is to find the length of the longest subsequence of a
given sequence such that all elements of the subsequence are sorted in increasing order. 

Let arr[0..n-1] be the input array and L(i) be the length of the LIS ending at index i such that arr[i] is the last
element of the LIS.
Then, L(i) can be recursively written as:
L(i) = 1 + max( L(j) ) where 0 < j < i and arr[j] < arr[i]; or
L(i) = 1, if no such j exists.
To find the LIS for a given array, we need to return max(L(i)) where 0 < i < n.

Note that the time complexity of the above Dynamic Programming (DP) solution is O(n^2) and there is a
O(nLogn) solution for the LIS problem.

Longest Increasing Subsequence Size (N log N)


The question is, when will it be safe to add or replace an element in the existing sequence?

Let us consider another sample  A = {2, 5, 3}. Say, the next element is 1. How can it extend the current
sequences {2,3} or {2, 5}. Obviously, it can’t extend either. Yet, there is a potential that the new smallest
element can be start of an LIS. To make it clear, consider the array is {2, 5, 3, 1, 2, 3, 4, 5, 6}. Making 1 as
new sequence will create new sequence which is largest.

The observation is, when we encounter new smallest element in the array, it can be a potential candidate to
start new sequence.

From the observations, we need to maintain lists of increasing sequences.


In general, we have set of  active lists  of varying length. We are adding an element A[i] to these lists. We
scan the lists (for end elements) in decreasing order of their length. We will verify the end elements of all the
lists to find a list whose end element is smaller than A[i] (floor value).

Our strategy determined by the following conditions,

1. If A[i] is smallest among all end


candidates of active lists, we will start
new active list of length 1.
2. If A[i] is largest among all end candidates of
active lists, we will clone the largest active
list, and extend it by A[i].
3. If A[i] is in between, we will find a list with
largest end element that is smaller than A[i].
Clone and extend this list by A[i]. We will discard all
other lists of same length as that of this modified list.

we have maintained the condition, “end element of smaller list is smaller than end elements of larger lists“

 Note that we are dealing with end elements only. We need not to maintain all the lists. We can store the end
elements in an array. Discarding operation can be simulated with replacement, and extending a list
is analogous to adding more elements to array.

To discard an element, we will trace ceil value of A[i] in auxiliary array (again observe the end elements in
your rough work), and replace ceil value with A[i]. We extend a list by adding element to auxiliary array. We
also maintain a counter to keep track of auxiliary array length.
The loop runs for N elements. In the worst case (what is worst case input?), we may end up querying ceil
value using binary search (log i) for many A[i].

Therefore, T(n) < O( log N! )  = O(N log N). Analyse to ensure that the upper and lower bounds are also O( N
log N ). The complexity is THETA (N log N).

‘Patience sorting’

static int LongestIncreasingSubsequenceLength(int A[], int size)


{
// Add boundary case, when array size is one

int[] tailTable = new int[size];


int len; // always points empty slot

tailTable[0] = A[0];
len = 1;
for (int i = 1; i < size; i++)
{
if (A[i] < tailTable[0])
// new smallest value
tailTable[0] = A[i];

else if (A[i] > tailTable[len-1])


// A[i] wants to extend largest subsequence
tailTable[len++] = A[i];

else
// A[i] wants to be current end candidate of an existing
// subsequence. It will replace ceil value in tailTable
tailTable[CeilIndex(tailTable, -1, len-1, A[i])] = A[i];
}

return len;
}

Cutting a Rod
Given a rod of length n inches and an array of prices that contains prices of all pieces of size smaller than
n.Determine the maximum value obtainable by cutting up the rod and selling the pieces. For example, if
length of the rod is 8 and the values of different pieces are given as following, then the maximum obtainable
value is 22 (by cutting in two pieces of lengths 2 and 6)

Let cutRoad(n) be the required (best possible price) value for a rod of lenght n. cutRod(n) can be written as
following.

cutRod(n) = max(price[i] + cutRod(n-i-1)) for all i in {0, 1 .. n-1}

Dynamic Programming | Set 25 (Subset Sum Problem)


Given a set of non-negative integers, and a value  sum, determine if there is a subset of the given set with
sum equal to given sum
isSubsetSum(set, n, sum) = isSubsetSum(set, n-1, sum) ||
isSubsetSum(set, n-1, sum-set[n-1])
Base Cases:
isSubsetSum(set, n, sum) = false, if sum > 0 and n == 0
isSubsetSum(set, n, sum) = true, if sum == 0

Time complexity of the above solution is O(sum*n)

Maximum Sum Increasing Subsequence


Given an array of n positive integers. Write a program to find the sum of maximum sum subsequence of the
given array such that the intgers in the subsequence are sorted in increasing order.
We need a slight change in the Dynamic Programming solution of LIS problem. All we need to change is to
use sum as a criteria instead of length of increasing subsequence.

Maximum Length Chain of Pairs)


You are given n pairs of numbers. In every pair, the first number is always smaller than the second number. A
pair (c, d) can follow another pair (a, b) if b < c. Chain of pairs can be formed in this fashion. Find the longest
chain which can be formed from a given set of pairs.

This problem is a variation of standard Longest Increasing Subsequence problem. Following is a simple two


step process.
1) Sort given pairs in increasing order of first (or smaller) element.
2) Now run a modified LIS process where we compare the second element of already finalized LIS with the
first element of new LIS being constructed.
Time Complexity: O(n^2) where n is the number of pairs.

The given problem is also a variation of Activity Selection problem and can be solved in (nLogn) time. To
solve it as a activity selection problem, consider the first element of a pair as start time in activity selection
problem, and the second element of pair as end time. 

Longest Common Substring


Given two strings ‘X’ and ‘Y’, find the length of the longest common substring.

Dynamic Programming can be used to find the longest common substring in O(m*n) time. The idea is to find
length of the longest common suffix for all substrings of both strings and store these lengths in a table.

The longest common suffix has following optimal substructure property


LCSuff(X, Y, m, n) = LCSuff(X, Y, m-1, n-1) + 1 if X[m-1] = Y[n-1]
0 Otherwise (if X[m-1] != Y[n-1])

The maximum length Longest Common Suffix is the longest common substring.
LCSubStr(X, Y, m, n) = Max(LCSuff(X, Y, i, j)) where 1 <= i <= m
and 1 <= j <= n

Time Complexity: O(m*n)


Auxiliary Space: O(m*n)
The longest substring can also be solved in O(n+m) time using Suffix Tree. 
Count all possible paths from top left to bottom right of a mXn
matrix
The problem is to count all the possible paths from top left to bottom right of a mXn matrix with the constraints
that from each cell you can either move only to right or down

count[i][j] = count[i-1][j] + count[i][j-1];


Time complexity of the above dynamic programming solution is O(mn).
Note the count can also be calculated using the formula (m-1 + n-1)!/(m-1)!(n-1)!

Program for nth Catalan Number


We have discussed a O(n) approach to find binomial coefficient nCr.
unsigned long int c = binomialCoeff(2*n, n);

// return 2nCn/(n+1)
return c/(n+1);

Count number of ways to reach a given score in a game


Consider a game where a player can score 3 or 5 or 10 points in a move. Given a total score n, find number
of ways to reach the given score.

This problem is a variation of coin change problem and can be solved in O(n) time and O(n) auxiliary space.

for (i=3; i<=n; i++)


table[i] += table[i-3];
for (i=5; i<=n; i++)
table[i] += table[i-5];
for (i=10; i<=n; i++)
table[i] += table[i-10];

Tiling Problem
Given a “2 x n” board and tiles of size “2 x 1”, count the number of ways to tile the given board using the 2 x 1
tiles. A tile can either be placed horizontally i.e., as a 1 x 2 tile or vertically i.e., as 2 x 1 tile.

Let “count(n)” be the count of ways to place tiles on a “2 x n” grid, we have following two ways to place first
tile.

1) If we place first tile vertically, the problem reduces to “count(n-1)”

2) If we place first tile horizontally, we have to place second tile also horizontally. So the problem reduces to
“count(n-2)”

Therefore, count(n) can be written as below.


count(n) = n if n = 1 or n = 2
count(n) = count(n-1) + count(n-2)
The above recurrence is noting but Fibonacci Number expression. We can find n’th Fibonacci number in
O(Log n) time,

Count the number of ways to tile the floor of size n x m using 1


x m size tiles
Given a floor of size n x m and tiles of size 1 x m. The problem is to count the number of ways to tile the given
floor using 1 x m tiles. A tile can either be placed horizontally or vertically.

Both n and m are positive integers and 2 < = m.

| 1, 1 < = n < m
count(n) = | 2, n = m
| count(n-1) + count(n-m), m < n

Count even length binary sequences with same sum of first and
second half bits
Given a number n, find count of all binary sequences of length 2n such that sum of first n bits is same as sum
of last n bits.

Input: n = 2
Output: 2
There are 6 sequences of length 2*n, the
sequences are 0101, 0110, 1010, 1001, 0000
and 1111

The idea is to fix first and last bits and then recur for n-1, i.e., remaining 2(n-1) bits. There are following
possibilities when we fix first and last bits.

1) First and last bits are same, remaining n-1 bits on both sides should also have the same sum.

2) First bit is 1 and last bit is 0, sum of remaining n-1 bits on left side should be 1 less than the sum n-1 bits
on right side.

2) First bit is 0 and last bit is 1, sum of remaining n-1 bits on left side should be 1 more than the sum n-1 bits
on right side.

diff is the expected difference between sum of first half digits and last half digits. Initially diff is 0.

// When first and last bits are same


// there are two cases, 00 and 11
count(n, diff) = 2*count(n-1, diff) +

// When first bit is 1 and last bit is 0


count(n-1, diff-1) +

// When first bit is 0 and last bit is 1


count(n-1, diff+1)
What should be base cases?
// When n == 1 (2 bit sequences)
1) If n == 1 and diff == 0, return 2
2) If n == 1 and |diff| == 1, return 1

// We can't cover difference of more than n with 2n bits


3) If |diff| > n, return 0

Below is O(n) solution for the same.

Number of n-bit strings with 0 ones = nC0


Number of n-bit strings with 1 ones = nC1
...
Number of n-bit strings with k ones = nCk
...
Number of n-bit strings with n ones = nCn
So, we can get required result using below

No. of 2*n bit strings such that first n bits have 0 ones &
last n bits have 0 ones = nC0 * nC0

No. of 2*n bit strings such that first n bits have 1 ones &
last n bits have 1 ones = nC1 * nC1

....
and so on.

Result = nC0*nC0 + nC1*nC1 + ... + nCn*nCn


= ∑(nCk)2
0 <= k <= n

int countSeq(int n)
{
int nCr=1, res = 1;

// Calculate SUM ((nCr)^2)


for (int r = 1; r<=n ; r++)
{
// Compute nCr using nC(r-1)
// nCr/nC(r-1) = (n+1-r)/r;
nCr = (nCr * (n+1-r))/r;

res += nCr*nCr;
}

return res;
}

Find number of solutions of a linear equation of n variables


Given a linear equation of n variables, find number of non-negative integer solutions of it. For example,let the
given equation be “x + 2y = 5”, solutions of this equation are “x = 1, y = 2”, “x = 5, y = 0” and “x = 1. It may be
assumed that all coefficients in given equation are positive integers.
If rhs = 0
countSol(coeff, 0, rhs, n-1) = 1
Else
countSol(coeff, 0, rhs, n-1) = ∑countSol(coeff, i, rhs-coeff[i], m-1)
where coeff[i]<=rhs and
i varies from 0 to n-1

int countSol(int coeff[], int n, int rhs)


{
// Create and initialize a table to store results of
// subproblems
int dp[rhs+1];
memset(dp, 0, sizeof(dp));
dp[0] = 1;

// Fill table in bottom up manner


for (int i=0; i<n; i++)
for (int j=coeff[i]; j<=rhs; j++)
dp[j] += dp[j-coeff[i]];

return dp[rhs];
}

Bell Numbers (Number of ways to Partition a Set)


Given a set of n elements, find number of ways of partitioning it.

Examples:

Input: n = 2
Output: Number of ways = 2
Explanation: Let the set be {1, 2}
{ {1}, {2} }
{ {1, 2} }

Compute nCr % p
Working of Above formula and Pascal Triangle:

Let us see how above formula works for C(4,3)

1==========>> n = 0, C(0,0) = 1

1–1========>> n = 1, C(1,0) = 1, C(1,1) = 1

1–2–1======>> n = 2, C(2,0) = 1, C(2,1) = 2, C(2,2) = 1

1–3–3–1====>> n = 3, C(3,0) = 1, C(3,1) = 3, C(3,2) = 3, C(3,3)=1

1–4–6–4–1==>> n = 4, C(4,0) = 1, C(4,1) = 4, C(4,2) = 6, C(4,3)=4, C(4,4)=1

C(n, r)%p = [ C(n-1, r-1)%p + C(n-1, r)%p ] % p


C(n, 0) = C(n, n) = 1
int nCrModp(int n, int r, int p)
{
// The array C is going to store last row of
// pascal triangle at the end. And last entry
// of last row is nCr
int C[r+1];
memset(C, 0, sizeof(C));

C[0] = 1; // Top row of Pascal Triangle

// One by constructs remaining rows of Pascal


// Triangle from top to bottom
for (int i = 1; i <= n; i++)
{
// Fill entries of current row using previous
// row values
for (int j = min(i, r); j > 0; j--)

// nCj = (n-1)Cj + (n-1)C(j-1);


C[j] = (C[j] + C[j-1])%p;
}
return C[r];
}
Time complexity of above solution is O(n*r) and it requires O(n) space. There are more and better solutions to
above problem.
Compute nCr % p | Set 2 (Lucas Theorem)
Lucas Theorem:

int nCrModpLucas(int n, int r, int p)


{
// Base case
if (r==0)
return 1;

// Compute last digits of n and r in base p


int ni = n%p, ri = r%p;
// Compute result for last digits computed above, and
// for remaining digits. Multiply the two results and
// compute the result of multiplication in modulo p.
return (nCrModpLucas(n/p, r/p, p) * // Last digits of n and r
nCrModpDP(ni, ri, p)) % p; // Remaining digits
}

Time Complexity:  Time complexity of this solution is O(p2  * Logp  n). There are O(Logp  n) digits in base p
representation of n. Each of these digits is smaller than p, therefore, computations for individual digits take
O(p2). Note that these computations are done using DP method which takes O(n*r) time.

Alternate Implementation with O(p2 + Logp n) time and O(p2) space:



The idea is to precompute Pascal triangle for size p x p and store it in 2D array. All values needed would now
take O(1) time. Therefore overall time complexity becomes O(p2 + Logp n).

Permutation Coefficient
Permutation refers to the process of arranging all the members of a given set to form a sequence. The
number of permutations on a set of n elements is given by n! , where “!” represents factorial.

The Permutation Coefficient represented by P(n, k) is used to represent the number of ways to obtain an
ordered subset having k elements from a set of n elements.

P(n, k) = P(n-1, k) + k* P(n-1, k-1)


we can apply dynamic programming here. the time complexity is O(n*k) and space complexity is O(n*k) as
the program uses an auxiliary matrix to store the result.

Can we do it in O(n) time ?


Let us suppose we maintain a single 1D array to compute the factorials up to n. We can use computed
factorial value and apply the formula P(n, k) = n! / (n-k)!. 
A O(n) time and O(1) Extra Space Solution

int PermutationCoeff(int n, int k)


{
int Fn = 1, Fk;

// Compute n! and (n-k)!


for (int i=1; i<=n; i++)
{
Fn *= i;
if (i == n-k)
Fk = Fn;
}
int coeff = Fn/Fk;
return coeff;
}

Count number of ways to fill a “n x 4” grid using “1 x 4”


tiles
Given a number n, count number of ways to fill a n x 4 grid using 1 x 4 tiles.
count(n) = 1 if n = 1 or n = 2 or n = 3
count(n) = 2 if n = 4
count(n) = count(n-1) + count(n-4)
Time Complexity : O(n)
Auxiliary Space : O(n)

A Space Optimized Solution of LCS


Given two strings, find the length of longest subsequence present in both of them.

if (i == 0 || j == 0)
L[i][j] = 0;

else if (X[i-1] == Y[j-1])


L[i][j] = L[i-1][j-1] + 1;

else
L[i][j] = max(L[i-1][j], L[i][j-1]);

One important observation in above simple implementation is, in each iteration of outer loop we only,
need values from all columns of previous row. So there is no need of storing all rows in our DP matrix, we
can just store two rows at a time and use them, in that way used space will reduce from L[m+1][n+1] to L[2]
[n+1]. Below is C++ implementation of this idea.
int lcs(string &X, string &Y)
{
// Find lengths of two strings
int m = X.length(), n = Y.length();

int L[2][n+1];
// Binary index, used to index current row and
// previous row.
bool bi;

for (int i=0; i<=m; i++)


{
// Compute current binary index
bi = i&1;

for (int j=0; j<=n; j++)


{
if (i == 0 || j == 0)
L[bi][j] = 0;

else if (X[i] == Y[j-1])


L[bi][j] = L[1-bi][j-1] + 1;

else
L[bi][j] = max(L[1-bi][j], L[bi][j-1]);
}
}

/* Last filled entry contains length of LCS


for X[0..n-1] and Y[0..m-1] */
return L[bi][n];
}

Time Complexity : O(m*n)


Auxiliary Space : O(n)
Find maximum length Snake sequence
Given a grid of numbers, find maximum length Snake sequence and print it. If multiple snake sequences
exists with the maximum length, print any one of them.

A snake sequence is made up of adjacent numbers in the grid such that for each number, the number on the
right or the number below it is +1 or -1 its value. For example, if you are at location (x, y) in the grid, you can
either move right i.e. (x, y+1) if that number is ± 1 or move down i.e. (x+1, y) if that number is ± 1.

Let  T[i][i]  represent maximum length of a snake which ends at cell (i, j), then for given matrix M, the DP
relation is defined as –

T[0][0] = 0

T[i][j] = max(T[i][j], T[i][j – 1] + 1) if M[i][j] = M[i][j – 1] ± 1

T[i][j] = max(T[i][j], T[i – 1][j] + 1) if M[i][j] = M[i – 1][j] ± 1

Time complexity of above solution is O(M*N). Auxiliary space used by above solution is O(M*N). If we are not
required to print the snake, space  can be further reduced to O(N) as we only uses the result from last row.

Minimum cost to fill given weight in a bag


You are given a bag of size W kg and you are provided costs of packets different weights of oranges in array
cost[] where cost[i] is basically cost of ‘i’ kg packet of oranges. Where cost[i] = -1 means that ‘i’ kg packet of
orange is unavailable

Find the minimum total cost to buy exactly W kg oranges and if it is not possible to buy exactly W kg oranges
then print -1. It may be assumed that there is infinite supply of all available packet types.

Input : W = 5, cost[] = {20, 10, 4, 50, 100}


Output : 14
We can choose two oranges to minimize cost. First
orange of 2Kg and cost 10. Second orange of 3Kg
and cost 4.

This problem is can be reduced to 0-1 Knapsack Problem. So in cost array, we first ignore those packets
which are not available i.e; cost is -1 and then traverse the cost array and create two array val[] for storing
cost of ‘i’ kg packet of orange and wt[] for storing weight of corresponding packet. Suppose cost[i] = 50 so
weight of packet will be i and cost will be 50.

Maximum weight path ending at any element of last row in


a matrix
Given a matrix of integers where every element represents weight of the cell. Find the path having the
maximum weight in matrix [N X N]. Path Traversal Rules are:
▪ It should begin from top left element.
▪ The path can end at any element of last row.
▪ We can move to following two cells from a cell (i, j).
1. Down Move : (i+1, j)
2. Diagonal Move : (i+1, j+1)
et maxCost(i, j) be the cost maximum cost to
reach mat[i][j]. Since end point can be any point
in last row, we finally return maximum of all values
maxCost(N-1, j) where j varies from 0 to N-1.

If i == 0 and j == 0
maxCost(0, 0) = mat[0][0]

// We can traverse through first column only by


// down move
Else if j = 0
maxCost(i, 0) = maxCost(i-1, 0) + mat[i][0]

// In other cases, a cell mat[i][j] can be reached


// through previous two cells ma[i-1][j] and
// mat[i-1][j-1]
Else
maxCost(i, j) = mat[i][j] + max(maxCost(i-1, j),
maxCost(i-1, j-1)),
Time complexity : O(N*N)
Recursively break a number in 3 parts to get maximum
sum
Given a number n, we can divide it in only three parts n/2, n/3 and n/4 (we will consider only integer part). The
task is to find the maximum sum we can make by dividing number in three parts recursively and summing up
them together.

for (int i=2; i<=n; i++)


dp[i] = max(dp[i/2] + dp[i/3] + dp[i/4], i);

return dp[n];

Time Complexity : O(n)


Auxiliary Space : O(n)

Maximum sum of pairs with specific difference


Given an array of integers and a number k. We can pair two number of array if difference between them is
strictly less than k. The task is to find maximum possible sum of disjoint pairs. Sum of P pairs is sum of all 2P
numbers of pairs.

Pair up i with (i-1)th element, i.e.


dp[i] = dp[i-2] + arr[i] + arr[i-1]
Don't pair up, i.e.
dp[i] = dp[i-1]

dp[i] = dp[i-1];

// if current and previous element can form a pair


if (arr[i] - arr[i-1] < K)
{
// update dp[i] by choosing maximum between
// pairing and not pairing
if (i >= 2)
dp[i] = max(dp[i], dp[i-2] + arr[i] + arr[i-1]);
else
dp[i] = max(dp[i], arr[i] + arr[i-1]);
}

Time complexity : O(N Log N)



Auxiliary Space : O(N)

An optimised solution contributed by Amit Sane is given below,


int maxSumPairWithDifferenceLessThanK(int arr[], int N, int k)
{
int maxSum = 0;

// Sort elements to ensure every i and i-1 is closest


// possible pair
sort(arr, arr+N);

// To get maximum possible sum, iterate from largest to


// smallest, giving larger numbers priority over smaller
// numbers.
for (int i=N-1; i>0; --i)
{
// Case I: Diff of arr[i] and arr[i-1] is less then K,
// add to maxSum
// Case II: Diff between arr[i] and arr[i-1] is not less
// then K, move to next i since with sorting we
// know, arr[i]-arr[i-1] < arr[i]-arr[i-2] and
// so on.
if (arr[i]-arr[i-1] < k)
{
//Assuming only positive numbers.
maxSum += arr[i];
maxSum += arr[i-1];

//When a match is found skip this pair


--i;
}
}

return maxSum;
}

Time complexity : O(N Log N)


Auxiliary Space : O(1)

Maximum subsequence sum such that no three are


consecutive
Given a sequence of positive numbers, find the maximum sum that can be formed which has no three
consecutive elements present.

This problem is mainly an extension of below problem.

Maximum sum such that no two elements are adjacent

sum[i] : Stores result for subarray arr[0..i], i.e.,


maximum possible sum in subarray arr[0..i]
such that no three elements are consecutive.

sum[0] = arr[0]

// Note : All elements are positive


sum[1] = arr[0] + arr[1]

// We have three cases


// 1) Exclude arr[2], i.e., sum[2] = sum[1]
// 2) Exclude arr[1], i.e., sum[2] = sum[0] + arr[2]
// 3) Exclude arr[0], i.e., sum[2] = arr[1] + arr[2]
sum[2] = max(sum[1], arr[0] + arr[2], arr[1] + arr[2])

In general,
// We have three cases
// 1) Exclude arr[i], i.e., sum[i] = sum[i-1]
// 2) Exclude arr[i-1], i.e., sum[i] = sum[i-2] + arr[i]
// 3) Exclude arr[i-2], i.e., sum[i-3] + arr[i] + arr[i-1]
sum[i] = max(sum[i-1], sum[i-2] + arr[i],
sum[i-3] + arr[i] + arr[i-1])

Time Complexity : O(n)



Auxiliary Space : O(n)

Longest subsequence such that difference between


adjacents is one
Given an array of n size, the task is to find the longest subsequence such that difference between adjacents
is one.

This problem is based upon the concept of Longest Increasing Subsequence Problem.

Let arr[0..n-1] be the input array and


dp[i] be the length of the longest subsequence (with
differences one) ending at index i such that arr[i]
is the last element of the subsequence.

Then, dp[i] can be recursively written as:


dp[i] = 1 + max(dp[j]) where 0 < j < i and
[arr[j] = arr[i] -1 or arr[j] = arr[i] + 1]
dp[i] = 1, if no such j exists.
To find the result for a given array, we need
to return max(dp[i]) where 0 < i < n.
Time Complexity: O(n2)
Auxiliary Space: O(n)

Maximum path sum for each position with jumps under


divisibility condition
Given an array of n positive integers. Initially we are at first position. We can jump to position y (1 <= x <= n)
from position x (1 <= x <= n) if x divides y and x < y. The task is to print maximum sum path ending at every
position x.

reate an 1-D array dp[] where each element dp[i]


stores maximum sum path ending at index i (or
position x where x = i+1) with divisible jumps.

The recurrence relation for dp[i] can be defined as:

dp[i] = max(dp[i], dp[divisor of i+1] + arr[i])

To find all the divisor of i+1, move from 1


divisor to sqrt(i+1).

Time Complexity: O(n*sqrt(n)).
Maximum sum Bi-tonic Sub-sequence
Given an array of integers. A subsequence of arr[] is called Bitonic if it is first increasing, then decreasing.

This problem is a variation of standard Longest Increasing Subsequence (LIS) problem and longest Bitonic


Sub-sequence.
We construct two arrays MSIBS[] and MSDBS[]. MSIBS[i] stores the sum of the Increasing subsequence
ending with arr[i]. MSDBS[i] stores the sum of the Decreasing subsequence starting from arr[i]. Finally, we
need to return the max sum of MSIBS[i] + MSDBS[i] – Arr[i].
Time complexity : O(n2)

LCS (Longest Common Subsequence) of three strings


Given 3 strings of all having length < 100,the task is to find the longest common sub-sequence in all three
given sequences.

Let the input sequences be X[0..m-1], Y[0..n-1] and Z[0..o-1] of lengths m, n and o respectively. And let
L(X[0..m-1], Y[0..n-1], Z[0..o-1]) be the lengths of LCS of the three sequences X, Y and Z. Following is the
implementation:

The idea is to take a 3D array to store the


length of common subsequence in all 3 given
sequences i. e., L[m + 1][n + 1][o + 1]
1- If any of the string is empty then there
is no common subsequence at all then
L[i][j][k] = 0

2- If the characters of all sequences match


(or X[i] == Y[j] ==Z[k]) then
L[i][j][k] = 1 + L[i-1][j-1][k-1]

3- If the characters of both sequences do


not match (or X[i] != Y[j] || X[i] != Z[k]
|| Y[j] !=Z[k]) then
L[i][j][k] = max(L[i-1][j][k],
L[i][j-1][k],
L[i][j][k-1])

Friends Pairing Problem


Given n friends, each one can remain single or can be paired up with some other friend. Each friend can be
paired only once. Find out the total number of ways in which friends can remain single or can be paired up.

f(n) = ways n people can remain single


or pair up.

For n-th person there are two choices:


1) n-th person remains single, we recur
for f(n-1)
2) n-th person pairs up with any of the
remaining n-1 persons. We get (n-1)*f(n-2)

Therefore we can recursively write f(n) as:


f(n) = f(n-1) + (n-1)*f(n-2)

Time Complexity : O(n)


Auxiliary Space : O(n)

Minimum steps to minimize n as per given condition


Given a number n, count minimum steps to minimize it to 1 according to the following criteria:

▪ If n is divisible by 2 then we may reduce n to n/2.


▪ If n is divisible by 3 then you may reduce n to n/3.
▪ Decrement n by 1.
Greedy Approach (Doesn’t work always) : 

As per greedy approach we may choose the step that makes n as low as possible and continue the same, till
it reaches 1.

while ( n > 1)
{
if (n % 3 == 0)
n /= 3;
else if (n % 2 == 0)
n /= 2;
else
n--;
steps++;
}
If we observe carefully, the greedy strategy doesn’t work here.

Eg: Given n = 10 , Greedy –> 10 /2 = 5 -1 = 4 /2 = 2 /2 = 1 ( 4 steps ).

But the optimal way is –> 10 -1 = 9 /3 = 3 /3 = 1 ( 3 steps ).

So, we must think of dynamic approach for optimal solution.

Dynamic Approach:

For finding minimum steps we have three possibilities for n and they are:

f(n) = 1 + f(n-1)
f(n) = 1 + f(n/2) // if n is divisible by 2
f(n) = 1 + f(n/3) // if n is divisible by 3

Gold Mine Problem


Given a gold mine of n*m dimensions. Each field in this mine contains a positive integer which is the amount
of gold in tons. Initially the miner is at first column but can be at any row. He can move only (right->,right
up /,right down\) that is from a given cell, the miner can move to the cell diagonally up towards the right or
right or diagonally down towards the right. Find out maximum amount of gold he can collect.

for (int col=n-1; col>=0; col--)


{
for (int row=0; row<m; row++)
{
// Gold collected on going to the cell on the right(->)
int right = (col==n-1)? 0: goldTable[row][col+1];

// Gold collected on going to the cell to right up (/)


int right_up = (row==0 || col==n-1)? 0:
goldTable[row-1][col+1];

// Gold collected on going to the cell to right down (\)


int right_down = (row==m-1 || col==n-1)? 0:
goldTable[row+1][col+1];

// Max gold collected from taking either of the


// above 3 paths
goldTable[row][col] = gold[row][col] +
max(right, max(right_up, right_down));
;
}
}

Time Complexity :O(m*n)


Space Complexity :O(m*n)
Find number of endless points
Given a binary N x N matrix, we need to find the total number of matrix positions from which there is an
endless path. Any position (i, j) is said to have an endless path if and only if all of the next positions in its
row(i) and its column(j) should have value 1. If any position next to (i,j) either in row(i) or in column(j) will have
0 then position (i,j) doesn’t have any endless path.

We can easily say that if there is a zero at any position, then it will block path for all the positions left to it and
top of it.Also, we can say that any position (i,j) will have an endless row if (i,j+1) will have an endless row and
value of (i,j) is 1.
Similarly, we can say that any position (i,j) will have an endless column if (i+1,j) will have an endless column
and value of (i,j) is 1.

So we should maintain two matrices one for row and one for column. Always start from right most position for
row and bottom most position for column and only check for next position whether it has endless path or not.
And Finally, if any position will have an endless path in both row and column matrix then that position is said
to have an endless path.

for (int j=0; j<n; j++)


{
// flag which will be zero once we get a '0'
// and it will be 1 otherwise
bool isEndless = 1;
for (int i=n-1; i>=0; i--)
{
// encountered a '0', set the isEndless
// variable to false
if (input[i][j] == 0)
isEndless = 0;
col[i][j] = isEndless;
}
}

Perfect Sum Problem (Print all subsets with given sum)


Given an array of integers and a sum, the task is to print all subsets of given array with sum equal to given
sum.

we build a 2D array dp[][] such that dp[i][j] stores true if sum j is possible with array elements from 0 to i.

After filling dp[][], we recursively traverse it from dp[n-1][sum]. For cell being traversed, we store path before
reaching it and consider two possibilities for the element.

1) Element is included in current path.

2) Element is not included in current path.

Whenever sum becomes 0, we stop the recursive calls and print current path.

http://www.geeksforgeeks.org/perfect-sum-problem-print-subsets-given-sum/

see code
Subset with sum divisible by m
Given a set of non-negative distinct integers, and a value m, determine if there is a subset of the given set
with sum divisible by m.

f n > m there will always be a subset with sum divisible by m (which is easy to prove with pigeonhole
principle). So we need to handle only cases of n <= m .

For n <= m we create a boolean DP table which will store the status of each value from 0 to m-1 which are
possible subset sum (modulo m) which have been encountered so far.

Now we loop through each element of given array arr[], and we add (modulo m) j which have DP[j] = true and
store all the such (j+arr[i])%m possible subset sum in a boolean array temp, and at the end of iteration over j,
we update DP table with temp. Also we add arr[i] to DP ie.. DP[arr[i]%m] = true.

In the end if DP[0] is true then it means YES there exist a subset with sum which is divisible by m, else NO.

Time Complexity : O(m^2)


Auxiliary Space : O(m)
Intermediate Problems :

0-1 Knapsack Problem


Given weights and values of n items, put these items in a knapsack of capacity W to get the maximum total
value in the knapsack.  In other words, given two integer arrays val[0..n-1] and wt[0..n-1] which represent
values and weights associated with n items respectively. Also given an integer W which represents knapsack
capacity, find out the maximum value subset of val[] such that sum of the weights of this subset is smaller
than or equal to W. You cannot break an item, either pick the complete item, or don’t pick it (0-1 property).

To consider all subsets of items, there can be two cases for every item: (1) the item is included in the optimal
subset, (2) not included in the optimal set.

Therefore, the maximum value that can be obtained from n items is max of following two values.

1) Maximum value obtained by n-1 items and W weight (excluding nth item).

2) Value of nth item plus maximum value obtained by n-1 items and W minus weight of the nth item (including
nth item).

If weight of nth item is greater than W, then the nth item cannot be included and case 1 is the only possibility.

Time Complexity: O(nW) where n is the number of items and W is the capacity of knapsack.
Length of the longest substring without repeating
characters
Given a string, find the length of the longest substring without repeating characters. For example, the longest
substrings without repeating characters for “ABDEFGABEF” are “BDEFGA” and “DEFGAB”, with length 6. 

The idea is to scan the string from left to right, keep track of the maximum length Non-Repeating Character
Substring (NRCS) seen so far. Let the maximum length be max_len. When we traverse the string, we also
keep track of length of the current NRCS using cur_len variable. For every new character, we look for it in
already processed part of the string (A temp array called visited[] is used for this purpose). If it is not present,
then we increase the cur_len by 1. If present, then there are two cases:

a) The previous instance of character is not part of current NRCS (The NRCS which is under process). In this
case, we need to simply increase cur_len by 1.

b)  If the previous instance is part of the current NRCS, then our current NRCS changes. It becomes the
substring staring from the next character of previous instance to currently scanned character. We also need to
compare cur_len and max_len, before changing current NRCS (or changing cur_len).

for(i = 1; i < n; i++)


{
prev_index = visited[str.charAt(i)];

/* If the current character is not present in


the already processed substring or it is not
part of the current NRCS, then do cur_len++ */
if(prev_index == -1 || i - cur_len > prev_index)
cur_len++;

/* If the current character is present in currently


considered NRCS, then update NRCS to start from
the next character of previous instance. */
else
{
/* Also, when we are changing the NRCS, we
should also check whether length of the
previous NRCS was greater than max_len or
not.*/
if(cur_len > max_len)
max_len = cur_len;

cur_len = i - prev_index;
}

// update the index of current character


visited[str.charAt(i)] = i;
}

Time Complexity: O(n + d) where n is length of the input string and d is number of characters in input string
alphabet. For example, if string consists of lowercase English characters then value of d is 26.
Auxiliary Space: O(d)

Count number of ways to reach destination in a Maze


Given a maze with obstacles, count number of paths to reach rightmost-bottommost cell from topmost-
leftmost cell. A cell in given maze has value -1 if it is a blockage or dead end, else 0.

From a given cell, we are allowed to move to cells (i+1, j) and (i, j+1) only.

We can recursively compute grid[i][j] using below


formula and finally return grid[R-1][C-1]

// If current cell is a blockage


if (maze[i][j] == -1)
maze[i][j] = -1; // Do not change

// If we can reach maze[i][j] from maze[i-1][j]


// then increment count.
else if (maze[i-1][j] > 0)
maze[i][j] = (maze[i][j] + maze[i-1][j]);

// If we can reach maze[i][j] from maze[i][j-1]


// then increment count.
else if (maze[i][j-1] > 0)
maze[i][j] = (maze[i][j] + maze[i][j-1]);

Super Ugly Number (Number whose prime factors are in


given set)
An efficient solution for this problem is similar to Method-2 of Ugly Number. Here is the algorithm :

1. Let k be size of given array of prime numbers.


2. Declare a set for super ugly numbers.
3. Insert first ugly number (which is always 1) into set.
4. Initialize array  multiple_of[k]  of size k with 0. Each element of this array is iterator for corresponding
prime in primes[k] array.
5. Initialize  nextMultipe[k]  array with primes[k]. This array behaves like next multiple variables of each
prime in given primes[k] array i.e; nextMultiple[i] = primes[i] * ugly[++multiple_of[i]].
6. Now loop until there are n elements in set ugly.

a). Find minimum among current multiples of primes in nextMultiple[] array and insert it in the set of ugly
numbers.

b). Then find this current minimum is multiple of which prime .

c). Increase iterator by 1 i.e; ++multiple_Of[i], for next multiple of current selected prime and update
nextMultiple for it.

Count number of ways to partition a set into k subsets


Given two numbers n and k where n represents number of elements in a set, find number of ways to partition
the set into k subsets.
Let S(n, k) be total number of partitions of n elements into k sets. Value of S(n, k) can be defined recursively
as,

S(n, k) = k*S(n-1, k) + S(n-1, k-1)


S(n, k) is called Stirling numbers of the second kind

When we add a (n+1)’th element to k partitions, there are two possibilities.


1) It is added as a single element set to existing partitions, i.e, S(n, k-1)
2) It is added to all sets of every partition, i.e., k*S(n, k)

Time Complexity: O(n x k)


Auxiliary Space: O(n x k)

Longest Palindromic Subsequence


Given a sequence, find the length of the longest palindromic subsequence in it. For example, if the given
sequence is “BBABCBCAB”, then the output should be 7 as “BABCBAB” is the longest palindromic
subseuqnce in it. “BBBBB” and “BBCBB” are also palindromic subsequences of the given sequence, but not
the longest ones.

L(i, i) = 1 for all indexes i in given sequence

// IF first and last characters are not same


If (X[i] != X[j]) L(i, j) = max{L(i + 1, j),L(i, j - 1)}

// If there are only 2 characters and both are same


Else if (j == i + 1) L(i, j) = 2

// If there are more than two characters, and first and last
// characters are same
Else L(i, j) = L(i + 1, j - 1) + 2
Time Complexity of the above implementation is O(n^2) which is much better than the worst case time
complexity of Naive Recursive implementation.

Egg Dropping Puzzle


The following is a description of the instance of this famous puzzle involving n=2 eggs and a building with
k=36 floors.
Suppose that we wish to know which stories in a 36-story building are safe to drop eggs from, and which will
cause the eggs to break on landing.

If only one egg is available and we wish to be sure of obtaining the right result, the experiment can be carried
out in only one way. Drop the egg from the first-floor window; if it survives, drop it from the second floor
window. Continue upward until it breaks. In the worst case, this method may require 36 droppings. Suppose 2
eggs are available. What is the least number of egg-droppings that is guaranteed to work in all cases?
The problem is not actually to find the critical floor, but merely to decide floors from which eggs should be
dropped so that total number of trials are minimized.

In this post, we will discuss solution to a general problem with n eggs and k floors. The solution is to try
dropping an egg from every floor (from 1 to k) and recursively calculate the minimum number of droppings
needed in worst case. The floor which gives the minimum value in worst case is going to be part of the
solution.

In the following solutions, we return the minimum number of trials in worst case; these solutions can be easily
modified to print floor numbers of every trials also.

1) Optimal Substructure:

When we drop an egg from a floor x, there can be two cases (1) The egg breaks (2) The egg doesn’t break.

1) If the egg breaks after dropping from xth floor, then we only need to check for floors lower than x with
remaining eggs; so the problem reduces to x-1 floors and n-1 eggs

2) If the egg doesn’t break after dropping from the xth floor, then we only need to check for floors higher than
x; so the problem reduces to k-x floors and n eggs.

Since we need to minimize the number of trials in  worst  case, we take the maximum of two cases. We
consider the max of above two cases for every floor and choose the floor which yields minimum number of
trials.

k ==> Number of floors


n ==> Number of Eggs
eggDrop(n, k) ==> Minimum number of trials needed to find the critical
floor in worst case.
eggDrop(n, k) = 1 + min{max(eggDrop(n - 1, x - 1), eggDrop(n, k - x)):
x in {1, 2, ..., k}}
Time Complexity: O(nk^2)
Auxiliary Space: O(nk)

Weighted Job Scheduling


Given N jobs where every job is represented by following three elements of it.

1. Start Time
2. Finish Time
3. Profit or Value Associated
Find the maximum profit subset of jobs such that no two jobs in the subset overlap.

A simple version of this problem is discussed  here  where every job has same profit or value. The  Greedy
Strategy for activity selection  doesn’t work here as a schedule with more jobs may have smaller profit or
value.

The above problem can be solved using following recursive solution.

1) First sort jobs according to finish time.


2) Now apply following recursive process.
// Here arr[] is array of n jobs
findMaximumProfit(arr[], n)
{
a) if (n == 1) return arr[0];
b) Return the maximum of following two profits.
(i) Maximum profit by excluding current job, i.e.,
findMaximumProfit(arr, n-1)
(ii) Maximum profit by including the current job
}

How to find the profit including current job?


The idea is to find the latest job before the current job (in
sorted array) that doesn't conflict with current job 'arr[n-1]'.
Once we find such a job, we recur for all jobs till that job and
add profit of current job to result.
In the above example, "job 1" is the latest non-conflicting
for "job 4" and "job 2" is the latest non-conflicting for "job 3".

Time Complexity of the above Dynamic Programming Solution is O(n2). Note that the above solution can be
optimized to O(nLogn) using Binary Search in latestNonConflict() instead of linear search.

Longest Bitonic Subsequence


Given an array arr[0 … n-1] containing n positive integers, a subsequence of arr[] is called Bitonic if it is first
increasing, then decreasing. Write a function that takes an array as argument and returns the length of the
longest bitonic subsequence.
This problem is a variation of standard Longest Increasing Subsequence (LIS) problem. Let the input array be
arr[] of length n. We need to construct two arrays lis[] and lds[] using Dynamic Programming solution of LIS
problem. lis[i] stores the length of the Longest Increasing subsequence ending with arr[i]. lds[i] stores the
length of the longest Decreasing subsequence starting from arr[i]. Finally, we need to return the max value of
lis[i] + lds[i] – 1 where i is from 0 to n-1.
Time Complexity: O(n^2)

Auxiliary Space: O(n)

Floyd Warshall Algorithm)


The  Floyd Warshall Algorithm  is for solving the All Pairs Shortest Path problem. The problem is to find
shortest distances between every pair of vertices in a given edge weighted directed Graph.

Floyd Warshall Algorithm



We initialize the solution matrix same as the input graph matrix as a first step. Then we update the solution
matrix by considering all vertices as an intermediate vertex. The idea is to one by one pick all vertices and
update all shortest paths which include the picked vertex as an intermediate vertex in the shortest path. When
we pick vertex number k as an intermediate vertex, we already have considered vertices {0, 1, 2, .. k-1} as
intermediate vertices. For every pair (i, j) of source and destination vertices respectively, there are two
possible cases.

1) k is not an intermediate vertex in shortest path from i to j. We keep the value of dist[i][j] as it is.

2) k is an intermediate vertex in shortest path from i to j. We update the value of dist[i][j] as dist[i][k] + dist[k][j].

for (k = 0; k < V; k++)


{
// Pick all vertices as source one by one
for (i = 0; i < V; i++)
{
// Pick all vertices as destination for the
// above picked source
for (j = 0; j < V; j++)
{
// If vertex k is on the shortest path from
// i to j, then update the value of dist[i][j]
if (dist[i][k] + dist[k][j] < dist[i][j])
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}

Time Complexity: O(V^3)

The above program only prints the shortest distances. We can modify the solution to print the shortest paths
also by storing the predecessor information in a separate 2D matrix.

Partition problem
Partition problem is to determine whether a given set can be partitioned into two subsets such that the sum of
elements in both subsets is same.
Following are the two main steps to solve this problem:

1) Calculate sum of the array. If sum is odd, there can not be two subsets with equal sum, so return false.

2) If sum of array elements is even, calculate sum/2 and find a subset of array with sum equal to sum/2.
The problem can be solved using dynamic programming when the sum of the elements is not too big. We can
create a 2D array part[][] of size (sum/2)*(n+1). And we can construct the solution in bottom up manner such
that every filled entry has following property

part[i][j] = true if a subset of {arr[0], arr[1], ..arr[j-1]} has sum


equal to i, otherwise false
Time Complexity: O(sum*n)
Auxiliary Space: O(sum*n)

Variations of LIS
We have discussed Dynamic Programming solution for Longest Increasing Subsequence problem in this post
and a O(nLogn) solution in this post. Following are commonly asked variations of the standard LIS problem.

1. Building Bridges: Consider a 2-D map with a horizontal river passing through its center. There are n
cities on the southern bank with x-coordinates a(1) … a(n) and n cities on the northern bank with x-
coordinates b(1) … b(n). You want to connect as many north-south pairs of cities as possible with bridges
such that no two bridges cross. When connecting cities, you can only connect city i on the northern bank
to city i on the southern bank.
Source: Dynamic Programming Practice Problems. The link also has well explained solution for the problem.

2. Maximum Sum Increasing Subsequence: Given an array of n positive integers. Write a program to find
the maximum sum subsequence of the given array such that the intgers in the subsequence are sorted in
increasing order. For example, if input is {1, 101, 2, 3, 100, 4, 5}, then output should be {1, 2, 3, 100}. The
solution to this problem has been published here.
3. The Longest Chain You are given pairs of numbers. In a pair, the first number is smaller with respect to
the second number. Suppose you have two sets (a, b) and (c, d), the second set can follow the first set if b <
c. So you can form a long chain in the similar fashion. Find the longest chain which can be formed. The
solution to this problem has been published here.

4. Box Stacking You are given a set of n types of rectangular 3-D boxes, where the i^th box has height h(i),
width w(i) and depth d(i) (all real numbers). You want to create a stack of boxes which is as tall as possible,
but you can only stack a box on top of another box if the dimensions of the 2-D base of the lower box are
each strictly larger than those of the 2-D base of the higher box. Of course, you can rotate a box so that any
side functions as its base. It is also allowable to use multiple instances of the same type of box.

Box Stacking Problem


You are given a set of n types of rectangular 3-D boxes, where the i^th box has height h(i), width w(i) and
depth d(i) (all real numbers). You want to create a stack of boxes which is as tall as possible, but you can only
stack a box on top of another box if the dimensions of the 2-D base of the lower box are each strictly larger
than those of the 2-D base of the higher box. Of course, you can rotate a box so that any side functions as its
base. It is also allowable to use multiple instances of the same type of box.

1) Generate all 3 rotations of all boxes. The size of rotation array becomes 3 times the size of original array.
For simplicity, we consider depth as always smaller than or equal to width.

2) Sort the above generated 3n boxes in decreasing order of base area.


3) After sorting the boxes, the problem is same as LIS with following optimal substructure property.

MSH(i) = Maximum possible Stack Height with box i at top of stack

MSH(i) = { Max ( MSH(j) ) + height(i) } where j < i and width(j) > width(i) and depth(j) > depth(i).

If there is no such j then MSH(i) = height(i)

4) To get overall maximum height, we return max(MSH(i)) where 0 < i < n

Time Complexity: O(n^2)


Auxiliary Space: O(n)

Bellman–Ford Algorithm
Given a graph and a source vertex src in graph, find shortest paths from src to all vertices in the given graph.
The graph may contain negative weight edges.

We have discussed Dijkstra’s algorithm for this problem. Dijksra’s algorithm is a Greedy algorithm and time
complexity is O(VLogV) (with the use of Fibonacci heap). Dijkstra doesn’t work for Graphs with negative
weight edges, Bellman-Ford works for such graphs. Bellman-Ford is also simpler than Dijkstra and suites well
for distributed systems. But time complexity of Bellman-Ford is O(VE), which is more than Dijkstra.

lgorithm

Following are the detailed steps.
Input: Graph and a source vertex src

Output: Shortest distance to all vertices from src. If there is a negative weight cycle, then shortest distances
are not calculated, negative weight cycle is reported.

1)  This step initializes distances from source to all vertices as infinite and distance to source itself as 0.
Create an array dist[] of size |V| with all values as infinite except dist[src] where src is source vertex.

2)  This step calculates shortest distances. Do following |V|-1 times where |V| is the number of vertices in
given graph.

…..a) Do following for each edge u-v

………………If dist[v] > dist[u] + weight of edge uv, then update dist[v]

………………….dist[v] = dist[u] + weight of edge uv

3) This step reports if there is a negative weight cycle in graph. Do following for each edge u-v

……If dist[v] > dist[u] + weight of edge uv, then “Graph contains negative weight cycle”

The idea of step 3 is, step 2 guarantees shortest distances if graph doesn’t contain negative weight cycle. If
we iterate through all edges one more time and get a shorter path for any vertex, then there is a negative
weight cycle

How does this work? Like other Dynamic Programming Problems, the algorithm calculate shortest paths in
bottom-up manner. It first calculates the shortest distances for the shortest paths which have at-most one
edge in the path. Then, it calculates shortest paths with at-nost 2 edges, and so on. After the ith iteration of
outer loop, the shortest paths with at most i edges are calculated. There can be maximum |V| – 1 edges in
any simple path, that is why the outer loop runs |v| – 1 times. The idea is, assuming that there is no negative
weight cycle, if we have calculated shortest paths with at most i edges, then an iteration over all edges
guarantees to give shortest path with at-most (i+1) edges (Proof is simple, you can refer  this  or  MIT Video
Lecture)

Notes

1)  Negative weights are found in various applications of graphs. For example, instead of paying cost for a
path, we may get some advantage if we follow the path.

2) Bellman-Ford works better (better than Dijksra’s) for distributed systems. Unlike Dijksra’s where we need to
find minimum value of all vertices, in Bellman-Ford, edges are considered one by one.

Optimal Binary Search Tree)


Given a sorted array  keys[0.. n-1]  of search keys and an array  freq[0.. n-1]  of frequency counts,
where freq[i] is the number of searches to keys[i]. Construct a binary search tree of all keys such that the total
cost of all the searches is as small as possible.

Let us first define the cost of a BST. The cost of a BST node is level of that node multiplied by its frequency.
Level of root is 1.
1) Optimal Substructure:

The optimal cost for freq[i..j] can be recursively calculated using following formula.


We need to calculate optCost(0, n-1) to find the result.

The idea of above formula is simple, we one by one try all nodes as root (r varies from i to j in second term).
When we make rth node as root, we recursively calculate optimal cost from i to r-1 and r+1 to j.

We add sum of frequencies from i to j (see first term in the above formula), this is added because every
search will go through root and one comparison will be done for every search.

Notes

1) The time complexity of the above solution is O(n^4). The time complexity can be easily reduced to O(n^3)
by pre-calculating sum of frequencies instead of calling sum() again and again.

2) In the above solutions, we have computed optimal cost only. The solutions can be easily modified to store
the structure of BSTs also. We can create another auxiliary array of size n to store the structure of tree. All we
need to do is, store the chosen ‘r’ in the innermost loop.

Largest Independent Set Problem)


Given a Binary Tree, find size of the  Largest  Independent  Set(LIS) in it. A subset of all tree nodes is an
independent set if there is no edge between any two nodes of the subset.

For example, consider the following binary tree. The largest independent set(LIS) is {10, 40, 60, 70, 80} and
size of the LIS is 5.

Let LISS(X) indicates size of largest independent set of a tree with root X.

LISS(X) = MAX { (1 + sum of LISS for all grandchildren of X),


(sum of LISS for all children of X) }
Time complexity of the above naive recursive approach is exponential. It should be noted that the above
function computes the same subproblems again and again. For example, LISS of node with value 50 is
evaluated for node with values 10 and 20 as 50 is grandchild of 10 and child of 20.
In the following solution, an additional field ‘liss’ is added to tree nodes. The initial value of ‘liss’ is set as 0 for
all nodes. The recursive function LISS() calculates ‘liss’ for a node only if it is not already set.

static class node


{
int data, liss;
node left, right;

public node(int data)


{
this.data = data;
this.liss = 0;
}
}
Time Complexity: O(n) where n is the number of nodes in given Binary tree.
Minimum insertions to form a palindrome
Given a string, find the minimum number of characters to be inserted to convert it to palindrome.

Let the input string be str[l……h]. The problem can be broken down into three parts:

1. Find the minimum number of insertions in the substring str[l+1,…….h].

2. Find the minimum number of insertions in the substring str[l…….h-1].

3. Find the minimum number of insertions in the substring str[l+1……h-1].

abcda: Number of insertions required is 2. adcbcda which is same as number of insertions in the substring
bcd(Why?).
Recursive Solution

The minimum number of insertions in the string str[l…..h] can be given as:

minInsertions(str[l+1…..h-1]) if str[l] is equal to str[h]

min(minInsertions(str[l…..h-1]), minInsertions(str[l+1…..h])) + 1 otherwise

for (gap = 1; gap < n; ++gap)


for (l = 0, h = gap; h < n; ++l, ++h)
table[l][h] = (str[l] == str[h])?
table[l+1][h-1] :
(Integer.min(table[l][h-1],
table[l+1][h]) + 1);

// Return minimum number of insertions


// for str[0..n-1]
return table[0][n-1];
}

Time complexity: O(N^2)


Auxiliary Space: O(N^2)

Another Dynamic Programming Solution (Variation of Longest Common Subsequence Problem)


The problem of finding minimum insertions can also be solved using Longest Common Subsequence (LCS)
Problem. If we find out LCS of string and its reverse, we know how many maximum characters can form a
palindrome. We need insert remaining characters. Following are the steps.
1) Find the length of LCS of input string and its reverse. Let the length be ‘l’.
2) The minimum number insertions needed is length of input string minus ‘l’.

Time complexity of this method is also O(n^2) and this method also requires O(n^2) extra space.

Minimum number of deletions to make a string palindrome


Given a string of size ‘n’. The task is to remove or delete minimum number of characters from the string so
that the resultant string is palindrome.

Note: The order of characters should be maintained.

An efficient approach uses the concept of finding the length of the longest palindromic subsequence of a given
sequence.

Algorithm:
-->str is the given string.
-->n length of str
-->len be the length of the longest
palindromic subsequence of str
-->// minimum number of deletions
min = n - len

Time Complexity: O(n2)

Maximum Product Cutting


Given a rope of length n meters, cut the rope in different parts of integer lengths in a way that maximizes
product of lengths of all parts. You must make at least one cut. Assume that the length of rope is more than 2
meters.
Input: n = 10
Output: 36 (Maximum obtainable product is 3*3*4)

This problem is similar to Rod Cutting Problem. We can get the maximum product by making a cut at different
positions and comparing the values obtained after a cut. We can recursively call the same function for a piece
obtained after a cut.

Let maxProd(n) be the maximum product for a rope of length n. maxProd(n) can be written as following.

maxProd(n) = max(i*(n-i), maxProdRec(n-i)*i) for all i in {1, 2, 3 .. n}

for (int i = 1; i <= n; i++)


{
int max_val = 0;
for (int j = 1; j <= i/2; j++)
max_val = max(max_val, (i-j)*j, j*val[i-j]);
val[i] = max_val;
}
return val[n];
Time Complexity of the Dynamic Programming solution is O(n^2) and it requires O(n) extra space.

A Tricky Solution:
If we see some examples of this problems, we can easily observe following pattern.
The maximum product can be obtained be repeatedly cutting parts of size 3 while size is greater than 4,
keeping the last part as size of 2 or 3 or 4. For example, n = 10, the maximum product is obtained by 3, 3, 4.
For n = 11, the maximum product is obtained by 3, 3, 3, 2

Clustering/Partitioning an array such that sum of square


differences is minimum
Given an array of n numbers and a number k. We need to divide the array into k partitions (clusters) of same
or different length. For a given k, there can be one or more ways to make clusters (partitions). We define a
function Cost(i) for the  cluster, as the square of the difference between its first and last element. If the current
cluster is , where  is the length of current cluster, then .

Amongst all the possible kinds of partitions, we have to find the partition that will minimize the function,
We create a table dp[n+1][k+1] table and initialize all values as infinite.

dp[i][j] stores optimal partition cost


for arr[0..i-1] and j partitions.
Let us compute the value of dp[i][j]. we take an index m, such that m < i, and put a partition next to that
position such that there is no slab in between the indices i and m. It can be seen simply that answer to the
current scenario is dp[m][j-1] + (a[i-1]-a[m])*(a[i-1]-a[m]), where the first term signifies the minimum f(x) till
the   element with j-1 partitions and the second one signifies the cost of current cluster. So we will take the
minimum of all the possible indices m and dp[i][j] will be assigned the minimum amongst them.

Time Complexity: Having the three simple loops, the complexity of the above algorithm is .

Count Derangements (Permutation such that no element


appears in its original position)
A Derangement is a permutation of n elements, such that no element appears in its original position. For
example, a derangement of {0, 1, 2, 3} is {2, 3, 1, 0}.

Given a number n, find total number of Derangements of a set of n elements.

Examples:

Input: n = 2
Output: 1
For two elements say {0, 1}, there is only one
possible derangement {1, 0}

Let countDer(n) be count of derangements for n elements. Below is recursive relation for it.

countDer(n) = (n-1)*[countDer(n-1) + countDer(n-2)]


How does above recursive relation work?

There are n – 1 ways for element 0 (this explains multiplication with n-1).

Let 0 be placed at index i. There are now two possibilities, depending on whether or not element i is placed
at 0 in return.

1. i is placed at 0: This case is equivalent to solving the problem for n-2 elements as two elements have
just swapped their positions.
2. i is not placed at 0: This case is equivalent to solving the problem for n-1 elements as now there are
n-1 elements, n-1 positions and every element has n-2 choices
Time Complexity: O(n)
Auxiliary Space: O(n)

Dice Throw)
Given n dice each with m faces, numbered from 1 to m, find the number of ways to get sum X. X is the
summation of values on each face when all the dice are thrown.
Let the function to find X from n dice is: Sum(m, n, X)
The function can be represented as:
Sum(m, n, X) = Finding Sum (X - 1) from (n - 1) dice plus 1 from nth dice
+ Finding Sum (X - 2) from (n - 1) dice plus 2 from nth dice
+ Finding Sum (X - 3) from (n - 1) dice plus 3 from nth dice
...................................................
...................................................
...................................................
+ Finding Sum (X - m) from (n - 1) dice plus m from nth dice

So we can recursively write Sum(m, n, x) as following


Sum(m, n, X) = Sum(m, n - 1, X - 1) +
Sum(m, n - 1, X - 2) +
.................... +
Sum(m, n - 1, X - m)

for (int i = 2; i <= n; i++)


for (int j = 1; j <= x; j++)
for (int k = 1; k <= m && k < j; k++)
tab

Time Complexity: O(m * n * x) where m is number of faces, n is number of dice and x is given sum.

We can add following two conditions at the beginning of findWays() to improve performance of program for
extreme cases (x is too high or x is too low)

// When x is so high that sum can not go beyond x even when we


// get maximum value in every dice throw.
if (m*n <= x)
return (m*n == x);

// When x is too low


if (n >= x)
return (n == x);
With above conditions added, time complexity becomes O(1) when x >= m*n or when x <= n. 

Optimal Strategy for a Game)


Problem statement: Consider a row of n coins of values v1 . . . vn, where n is even. We play a game against
an opponent by alternating turns. In each turn, a player selects either the first or last coin from the row,
removes it from the row permanently, and receives the value of the coin. Determine the maximum possible
amount of money we can definitely win if we move first.

There are two choices:


1. The user chooses the ith coin with value Vi: The opponent either chooses (i+1)th coin or jth coin. The
opponent intends to choose the coin which leaves the user with minimum value.
i.e. The user can collect the value Vi + min(F(i+2, j), F(i+1, j-1) )
2. The user chooses the jth coin with value Vj: The opponent either chooses ith coin or (j-1)th coin. The
opponent intends to choose the coin which leaves the user with minimum value.
i.e. The user can collect the value Vj + min(F(i+1, j-1), F(i, j-2) )

Following is recursive solution that is based on above two choices. We take the maximum of two choices.
F(i, j) represents the maximum value the user can collect from
i'th coin to j'th coin.

F(i, j) = Max(Vi + min(F(i+2, j), F(i+1, j-1) ),


Vj + min(F(i+1, j-1), F(i, j-2) ))
Base Cases
F(i, j) = Vi If j == i
F(i, j) = max(Vi, Vj) If j == i+1

Word Break Problem)


Given an input string and a dictionary of words, find out if the input string can be segmented into a space-
separated sequence of dictionary words. See following examples for more details.

This is a famous Google interview question, also being asked by many other companies now a days.

Consider the following dictionary


{ i, like, sam, sung, samsung, mobile, ice,
cream, icecream, man, go, mango}

Input: ilike
Output: Yes
The string can be segmented as "i like".

Input: ilikesamsung
Output: Yes
The string can be segmented as "i like samsung"
or "i like sam sung".

// A Dynamic Programming based program to test whether a given string can


// be segmented into space separated words in dictionary
#include <iostream>
#include <string.h>
using namespace std;

/* A utility function to check whether a word is present in dictionary or not.


An array of strings is used for dictionary. Using array of strings for
dictionary is definitely not a good idea. We have used for simplicity of
the program*/
int dictionaryContains(string word)
{
string dictionary[] = {"mobile","samsung","sam","sung","man","mango",
"icecream","and","go","i","like","ice","cream"};
int size = sizeof(dictionary)/sizeof(dictionary[0]);
for (int i = 0; i < size; i++)
if (dictionary[i].compare(word) == 0)
return true;
return false;
}

// Returns true if string can be segmented into space separated


// words, otherwise returns false
bool wordBreak(string str)
{
int size = str.size();
if (size == 0) return true;

// Create the DP table to store results of subroblems. The value wb[i]


// will be true if str[0..i-1] can be segmented into dictionary words,
// otherwise false.
bool wb[size+1];
memset(wb, 0, sizeof(wb)); // Initialize all values as false.

for (int i=1; i<=size; i++)


{
// if wb[i] is false, then check if current prefix can make it true.
// Current prefix is "str.substr(0, i)"
if (wb[i] == false && dictionaryContains( str.substr(0, i) ))
wb[i] = true;

// wb[i] is true, then check for all substrings starting from


// (i+1)th character and store their results.
if (wb[i] == true)
{
// If we reached the last prefix
if (i == size)
return true;

for (int j = i+1; j <= size; j++)


{
// Update wb[j] if it is false and can be updated
// Note the parameter passed to dictionaryContains() is
// substring starting from index 'i' and length 'j-i'
if (wb[j] == false && dictionaryContains( str.substr(i, j-i) ))
wb[j] = true;

// If we reached the last character


if (j == size && wb[j] == true)
return true;
}
}
}

The above solutions only finds out whether a given string can be segmented or not. Extend the above
Dynamic Programming solution to print all possible partitions of input string.
Refer below post for solution of exercise.
Word Break Problem using Backtracking

Remove minimum elements from either side such that


2*min becomes more than max
Given an unsorted array, trim the array such that twice of minimum is greater than maximum in the trimmed
array. Elements should be removed either end of the array.

Number of removals should be minimum.

minRemovals(int arr[], int l, int h)


1) Find min and max in arr[l..h]
2) If 2*min > max, then return 0.
3) Else return minimum of "minRemovals(arr, l+1, h) + 1"
and "minRemovals(arr, l, h-1) + 1"

for (gap = 0; gap < n; ++gap)


{
for (i = 0, j = gap; j < n; ++i, ++j)
{
mn = min(arr, i, j);
mx = max(arr, i, j);
table[i][j] = (2*mn > mx)? 0: min(table[i][j-1]+1,
table[i+1][j]+1);
}
}

ime Complexity: O(n3) where n is the number of elements in arr[].

Further Optimizations:

The above code can be optimized in many ways.

1)  We can avoid calculation of min() and/or max() when min and/or max is/are not changed by removing
corner elements.

2) We can pre-process the array and build segment tree in O(n) time. After the segment tree is built, we can
query range minimum and maximum in O(Logn) time. The overall time complexity is reduced to O(n2Logn)
time.

A O(n^2) Solution
The idea is to find the maximum sized subarray such that 2*min > max. We run two nested loops, the outer
loop chooses a starting point and the inner loop chooses ending point for the current starting point. We keep
track of longest subarray with the given property.

for (int start=0; start<n; start++)


{
// Initialize min and max for the current start
int min = INT_MAX, max = INT_MIN;

// Choose different ending points for current start


for (int end = start; end < n; end ++)
{
// Update min and max if necessary
int val = arr[end];
if (val < min) min = val;
if (val > max) max = val;

// If the property is violated, then no


// point to continue for a bigger array
if (2 * min <= max) break;

// Update longest_start and longest_end if needed


if (end - start > longest_end - longest_start ||
longest_start == -1)
{
longest_start = start;
longest_end = end;
}
}
}

Count number of binary strings without consecutive 1’s


Given a positive integer N, count all possible distinct binary strings of length N such that there are no
consecutive 1’s.
This problem can be solved using Dynamic Programming. Let a[i] be the number of binary strings of length i
which do not contain any two consecutive 1’s and which end in 0. Similarly, let b[i] be the number of such
strings which end in 1. We can append either 0 or 1 to a string ending in 0, but we can only append 0 to a
string ending in 1. This yields the recurrence relation:

a[i] = a[i - 1] + b[i - 1]


b[i] = a[i - 1]
return a[n-1] + b[n-1]

If we take a closer look at the pattern, we can observe that the count is actually (n+2)’th Fibonacci number for
n >= 1. The Fibonacci Numbers are 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 141, ….

n = 1, count = 2 = fib(3)
n = 2, count = 3 = fib(4)
n = 3, count = 5 = fib(5)
n = 4, count = 8 = fib(6)
n = 5, count = 13 = fib(7)
Therefore we can count the strings in O(Log n) time also using the method 5 here.

Count Possible Decodings of a given Digit Sequence


Let 1 represent ‘A’, 2 represents ‘B’, etc. Given a digit sequence, count the number of possible decodings of
the given digit sequence.
Examples:

Input: digits[] = "121"


Output: 3
// The possible decodings are "ABA", "AU", "LA"

This problem is recursive and can be broken in sub-problems. We start from end of the given digit sequence.
We initialize the total count of decodings as 0. We recur for two subproblems.
1) If the last digit is non-zero, recur for remaining (n-1) digits and add the result to total count.
2) If the last two digits form a valid character (or smaller than 27), recur for remaining (n-2) digits and add the
result to total count.
 the recursive solution is similar to Fibonacci Numbers. Therefore, we can optimize the above solution to work
in O(n) time using Dynamic Programming.
Time Complexity of the above solution is O(n) and it requires O(n) auxiliary space. We can reduce auxiliary
space to O(1) by using space optimized version discussed in the Fibonacci Number Post.

Count all possible walks from a source to a destination


with exactly k edges
Given a directed graph and two vertices ‘u’ and ‘v’ in it, count all possible walks from ‘u’ to ‘v’ with exactly k
edges on the walk.

The graph is given as adjacency matrix representation where value of graph[i][j] as 1 indicates that there is an
edge from vertex i to vertex j and a value 0 indicates no edge from i to j.
The idea is to build a 3D table where first dimension is source, second dimension is destination, third
dimension is number of edges from source to destination, and the value is count of walks. Like other Dynamic
Programming problems, we fill the 3D table in bottom up manner.

int count[][][] = new int[V][V][k+1];

// Loop for number of edges from 0 to k


for (int e = 0; e <= k; e++)
{
for (int i = 0; i < V; i++) // for source
{
for (int j = 0; j < V; j++) // for destination
{
// initialize value
count[i][j][e] = 0;

// from base cases


if (e == 0 && i == j)
count[i][j][e] = 1;
if (e == 1 && graph[i][j]!=0)
count[i][j][e] = 1;

// go to adjacent only when number of edges


// is more than 1
if (e > 1)
{
for (int a = 0; a < V; a++) // adjacent of i
if (graph[i][a]!=0)
count[i][j][e] += count[a][j][e-1];
}
}
}
}
return count[u][v][k];

Time complexity of the above DP based solution is O(V3K) which is much better than the naive solution.

We can also use Divide and Conquer to solve the above problem in O(V3Logk) time. The count of walks of
length k from u to v is the [u][v]’th entry in (graph[V][V])k. We can calculate power of by doing O(Logk)
multiplication by using the  divide and conquer technique to calculate power. A multiplication between two
matrices of size V x V takes O(V3) time. Therefore overall time complexity of this method is O(V3Logk).

Shortest path with exactly k edges in a directed and


weighted graph
Given a directed and two vertices ‘u’ and ‘v’ in it, find shortest path from ‘u’ to ‘v’ with exactly k edges on the
path.

for (int a = 0; a < V; a++)


{
// There should be an edge from i to a and
// a should not be same as either i or j
if (graph[i][a] != INF && i != a &&
j!= a && sp[a][j][e-1] != INF)
sp[i][j][e] = Math.min(sp[i][j][e],
graph[i][a] + sp[a][j][e-1]);
}
Time complexity of the above DP based solution is O(V3K) which is much better than the naive solution.

Longest Even Length Substring such that Sum of First and


Second Half is same
Given a string ‘str’ of digits, find length of the longest substring of ‘str’, such that the length of the substring is
2k digits and sum of left k digits is equal to the sum of right k digits.

[A O(n2) and O(n) extra space solution]



The idea is to use a single dimensional array to store cumulative sum.

// A O(n^2) time and O(n) extra space solution


#include<bits/stdc++.h>
using namespace std;

int findLength(string str, int n)


{
int sum[n+1]; // To store cumulative sum from first digit to nth digit
sum[0] = 0;

/* Store cumulative sum of digits from first to last digit */


for (int i = 1; i <= n; i++)
sum[i] = (sum[i-1] + str[i-1] - '0'); /* convert chars to int */

int ans = 0; // initialize result

/* consider all even length substrings one by one */


for (int len = 2; len <= n; len += 2)
{
for (int i = 0; i <= n-len; i++)
{
int j = i + len - 1;

/* Sum of first and second half is same than update ans */


if (sum[i+len/2] - sum[i] == sum[i+len] - sum[i+len/2])
ans = max(ans, len);
}
}
return an
A O(n2) time and O(1) extra space solution]
The idea is to consider all possible mid points (of even length substrings) and keep expanding on both sides
to get and update optimal length as the sum of two sides become equal.
for (int i = 0; i <= n-2; i++)
{
/* For current midpoint 'i', keep expanding substring on
both sides, if sum of both sides becomes equal update
ans */
int l = i, r = i + 1;

/* initialize left and right sum */


int lsum = 0, rsum = 0;

/* move on both sides till indexes go out of bounds */


while (r < n && l >= 0)
{
lsum += str[l] - '0';
rsum += str[r] - '0';
if (lsum == rsum)
ans = max(ans, r-l+1);
l--;
r++;
}
}
return ans;

Vertex Cover Problem


A vertex cover of an undirected graph is a subset of its vertices such that for every edge (u, v) of the graph,
either ‘u’ or ‘v’ is in vertex cover. Although the name is Vertex Cover, the set covers all edges of the given
graph.
The problem to find minimum size vertex cover of a graph is NP complete. But it can be solved in polynomial
time for trees. In this post a solution for Binary Tree is discussed. The same solution can be extended for n-
ary trees.

The idea is to consider following two possibilities for root and recursively for all nodes down the root.

1) Root is part of vertex cover: In this case root covers all children edges. We recursively calculate size of
vertex covers for left and right subtrees and add 1 to the result (for root).

2) Root is not part of vertex cover: In this case, both children of root must be included in vertex cover to
cover all root to children edges. We recursively calculate size of vertex covers of all grandchildren and
number of children to the result (for two children of root).

In the following solution, an additional field ‘vc’ is added to tree nodes. The initial value of ‘vc’ is set as 0 for all
nodes. The recursive function vCover() calculates ‘vc’ for a node only if it is not already set.
Find the minimum cost to reach destination using a train
There are N stations on route of a train. The train goes from station 0 to N-1. The ticket cost for all pair of
stations (i, j) is given where j is greater than i. Find the minimum cost to reach the destination.

Input:
cost[N][N] = { {0, 15, 80, 90},
{INF, 0, 40, 50},
{INF, INF, 0, 70},
{INF, INF, INF, 0}
};
There are 4 stations and cost[i][j] indicates cost to reach j
from i. The entries where j < i are meaningless.

Output:
The minimum cost is 65
The minimum cost can be obtained by first going to station 1
from 0. Then from station 1 to station 3.

minCost(0, N-1) = MIN { cost[0][n-1],


cost[0][1] + minCost(1, N-1),
minCost(0, 2) + minCost(2, N-1),
........,
minCost(0, N-2) + cost[N-2][n-1] }
We can solve this problem using O(N) extra space and O(N2) time. The idea is based on the fact that given
input matrix is a Directed Acyclic Graph (DAG). The shortest path in DAG can be calculated using the
approach discussed in below post.

Shortest Path in Directed Acyclic Graph

We need to do less work here compared to above mentioned post as we know  topological sorting  of the
graph. The topological sorting of vertices here is 0, 1, ..., N-1. Following is the idea once topological sorting is
known.

The idea in below code is to first calculate min cost for station 1, then for station 2, and so on. These costs
are stored in an array dist[0...N-1].

1) The min cost for station 0 is 0, i.e., dist[0] = 0

2) The min cost for station 1 is cost[0][1], i.e., dist[1] = cost[0][1]

3) The min cost for station 2 is minimum of following two.



     a) dist[0] + cost[0][2]

     b) dist[1] + cost[1][2]

3) The min cost for station 3 is minimum of following three.



     a) dist[0] + cost[0][3]

     b) dist[1] + cost[1][3]

     c) dist[2] + cost[2][3]

Similarly, dist[4], dist[5], ... dist[N-1] are calculated.

int dist[] = new int[N];


for (int i=0; i<N; i++)
dist[i] = INF;
dist[0] = 0;

// Go through every station and check if using it


// as an intermediate station gives better path
for (int i=0; i<N; i++)
for (int j=i+1; j<N; j++)
if (dist[j] > dist[i] + cost[i][j])
dist[j] = dist[i] + cost[i][j];

return dist[N-1];
}

Maximum profit by buying and selling a share at most twice


In a daily share trading, a buyer buys shares in the morning and sells it on same day. If the trader is allowed
to make at most 2 transactions in a day, where as second transaction can only start after first one is complete
(Sell->buy->sell->buy). Given stock prices throughout day, find out maximum profit that a share trader could
have made.
Max profit with at most two transactions =
MAX {max profit with one transaction and subarray price[0..i] +
max profit with one transaction and aubarray price[i+1..n-1] }
i varies from 0 to n-1.

We can do this O(n) using following Efficient Solution. The idea is to store maximum possible profit of every
subarray and solve the problem in following two phases.

1) Create a table profit[0..n-1] and initialize all values in it 0.

2)  Traverse price[] from right to left and update profit[i] such that profit[i] stores maximum profit achievable
from one transaction in subarray price[i..n-1]

3)  Traverse price[] from left to right and update profit[i] such that profit[i] stores maximum profit such that
profit[i] contains maximum achievable profit from two transactions in subarray price[0..i].

4) Return profit[n-1]

To do step 1, we need to keep track of maximum price from right to left side and to do step 2, we need to
keep track of minimum price from left to right. Why we traverse in reverse directions? The idea is to save
space, in second step, we use same array for both purposes, maximum with 1 transaction and maximum with
2 transactions. After an iteration i, the array profit[0..i] contains maximum profit with 2 transactions and
profit[i+1..n-1] contains profit with two transactions.
int max_price = price[n-1];
for (int i=n-2;i>=0;i--)
{
// max_price has maximum of price[i..n-1]
if (price[i] > max_price)
max_price = price[i];

// we can get profit[i] by taking maximum of:


// a) previous maximum, i.e., profit[i+1]
// b) profit by buying at price[i] and selling at
// max_price
profit[i] = Math.max(profit[i+1], max_price-price[i]);
}

/* Get the maximum profit with two transactions allowed


After this loop, profit[n-1] contains the result */
int min_price = price[0];
for (int i=1; i<n; i++)
{
// min_price is minimum price in price[0..i]
if (price[i] < min_price)
min_price = price[i];

// Maximum profit is maximum of:


// a) previous maximum, i.e., profit[i-1]
// b) (Buy, Sell) at (min_price, price[i]) and add
// profit of other trans. stored in profit[i]
profit[i] = Math.max(profit[i-1], profit[i] +
(price[i]-min_price) );
}
int result = profit[n-1];
return result;
Time complexity of the above solution is O(n).

Count possible ways to construct buildings


Given an input number of sections and each section has 2 plots on either sides of the road. Find all possible
ways to construct buildings in the plots such that there is a space between any 2 buildings.

N = 3
Output = 25
3 sections, which means possible ways for one side are
BSS, BSB, SSS, SBS, SSB where B represents a building
and S represents an empty space
Total possible ways are 25, because a way to place on
one side can correspond to any of 5 ways on other side.
We can simplify the problem to first calculate for one side only. If we know the result for one side, we can
always do square of the result and get result for two sides.

Let countB(i) be count of possible ways with i sections


ending with a building.
countS(i) be count of possible ways with i sections
ending with a space.

// A space can be added after a building or after a space.


countS(N) = countB(N-1) + countS(N-1)
// A building can only be added after a space.
countB[N] = countS(N-1)

// Result for one side is sum of the above two counts.


result1(N) = countS(N) + countB(N)

// Result for two sides is square of result1(N)


result2(N) = result1(N) * result1(N)
Time complexity: O(N)

Auxiliary Space: O(1)

int countB=1, countS=1, prev_countB, prev_countS;

// Use the above recursive formula for calculating


// countB and countS using previous values
for (int i=2; i<=N; i++)
{
prev_countB = countB;
prev_countS = countS;

countS = prev_countB + prev_countS;


countB = prev_countS;
}

Compute sum of digits in all numbers from 1 to n


Given a number x, find sum of digits in all numbers from 1 to n.

Examples:
Input: n = 5
Output: Sum of digits in numbers from 1 to 5 = 15
Let us take few examples.

sum(9) = 1 + 2 + 3 + 4 ........... + 9
= 9*10/2
= 45

sum(99) = 45 + (10 + 45) + (20 + 45) + ..... (90 + 45)


= 45*10 + (10 + 20 + 30 ... 90)
= 45*10 + 10(1 + 2 + ... 9)
= 45*10 + 45*10
= sum(9)*10 + 45*10

sum(999) = sum(99)*10 + 45*100


In general, we can compute sum(10d – 1) using below formula

sum(10d - 1) = sum(10d-1 - 1) * 10 + 45*(10d-1)

Algorithm: sum(n)

1) Find number of digits minus one in n. Let this value be 'd'.


For 328, d is 2.

2) Compute some of digits in numbers from 1 to 10d - 1.


Let this sum be w. For 328, we compute sum of digits from 1 to
99 using above formula.
3) Find Most significant digit (msd) in n. For 328, msd is 3.

4) Overall sum is sum of following terms

a) Sum of digits in 1 to "msd * 10d - 1". For 328, sum of


digits in numbers from 1 to 299.
For 328, we compute 3*sum(99) + (1 + 2)*100. Note that sum of
sum(299) is sum(99) + sum of digits from 100 to 199 + sum of digits
from 200 to 299.
Sum of 100 to 199 is sum(99) + 1*100 and sum of 299 is sum(99) + 2*100.
In general, this sum can be computed as w*msd + (msd*(msd-1)/2)*10d

b) Sum of digits in msd * 10d to n. For 328, sum of digits in


300 to 328.
For 328, this sum is computed as 3*29 + recursive call "sum(28)"
In general, this sum can be computed as msd * (n % (msd*10d) + 1)
+ sum(n % (10d))

Shortest Common Supersequence


Given two strings str1 and str2, find the shortest string that has both str1 and str2 as subsequences.

Length of the shortest supersequence = (Sum of lengths of given two strings) -


(Length of LCS of two given strings)
(Alternate) A simple analysis yields below simple recursive solution.

Let X[0..m-1] and Y[0..n-1] be two strings and m and be respective


lengths.

if (m == 0) return n;
if (n == 0) return m;

// If last characters are same, then add 1 to result and


// recur for X[]
if (X[m-1] == Y[n-1])
return 1 + SCS(X, Y, m-1, n-1);

// Else find shortest of following two


// a) Remove last character from X and recur
// b) Remove last character from Y and recur
else return 1 + min( SCS(X, Y, m-1, n), SCS(X, Y, m, n-1) );

Find minimum number of coins that make a given value


Given a value V, if we want to make change for V cents, and we have infinite supply of each of C = { C1,
C2, .. , Cm} valued coins, what is the minimum number of coins to make the change?

This problem is a variation of the problem discussed  Coin Change Problem. Here instead of finding total
number of possible solutions, we need to find the solution with minimum number of coins.
The minimum number of coins for a value V can be computed using below recursive formula.

If V == 0, then 0 coins required.


If V > 0
minCoin(coins[0..m-1], V) = min {1 + minCoins(V-coin[i])}
where i varies from 0 to m-1
and coin[i] <= V
Time complexity of the above solution is O(mV).

Minimum number of squares whose sum equals to given


number n
A number can always be represented as a sum of squares of other numbers. Note that 1 is a square and we
can always break a number as (1*1 + 1*1 + 1*1 + …). Given a number n, find the minimum number of
squares that sum to X.

If n <= 3, then return n


Else
minSquares(n) = min {1 + minSquares(n - x*x)}
where x >= 1 and x*x <= n

Find length of the longest consecutive path from a given


starting character
Given a matrix of characters. Find length of the longest path from a given character, such that all characters
in the path are consecutive to each other, i.e., every character in path is next to previous in alphabetical
order. It is allowed to move in all 8 directions from a cell.

The idea is to first search given starting character in the given matrix. Do Depth First Search (DFS) from all
occurrences to find all consecutive paths. While doing DFS, we may encounter many subproblems again and
again. So we use dynamic programming to store results of subproblems.
static int getLenUtil(char mat[][], int i, int j, char prev)
{
// If this cell is not valid or current character is not
// adjacent to previous one (e.g. d is not adjacent to b )
// or if this cell is already included in the path than return 0.
if (!isvalid(i, j) || !isadjacent(prev, mat[i][j]))
return 0;

// If this subproblem is already solved , return the answer


if (dp[i][j] != -1)
return dp[i][j];

int ans = 0; // Initialize answer

// recur for paths with differnt adjacent cells and store


// the length of longest path.
for (int k=0; k<8; k++)
ans = Math.max(ans, 1 + getLenUtil(mat, i + x[k],
j + y[k], mat[i][j]));

// save the answer and return


return dp[i][j] = ans;
}

// Returns length of the longest path with all characters consecutive


// to each other. This function first initializes dp array that
// is used to store results of subproblems, then it calls
// recursive DFS based function getLenUtil() to find max length path
static int getLen(char mat[][], char s)
{
//assigning all dp values to -1
for(int i = 0;i<R;++i)
for(int j = 0;j<C;++j)
dp[i][j] = -1;

int ans = 0;

for (int i=0; i<R; i++)


{
for (int j=0; j<C; j++)
{
// check for each possible starting point
if (mat[i][j] == s) {

// recur for all eight adjacent cells


for (int k=0; k<8; k++)
ans = Math.max(ans, 1 + getLenUtil(mat,
i + x[k], j + y[k], s));
}
}
}
return ans;
}
Total number of non-decreasing numbers with n digits
A number is non-decreasing if every digit (except the first one) is greater than or equal to previous digit. For
example, 223, 4455567, 899, are non-decreasing numbers.

One way to look at the problem is, count of numbers is equal to count n digit number ending with 9 plus count
of ending with digit 8 plus count for 7 and so on. How to get count ending with a particular digit? We can recur
for n-1 length and digits smaller than or equal to the last digit. So below is recursive formula.

Count of n digit numbers = (Count of (n-1) digit numbers Ending with digit 9) +
(Count of (n-1) digit numbers Ending with digit 8) +
.............................................+
.............................................+
(Count of (n-1) digit numbers Ending with digit 0)
Let count ending with digit ‘d’ and length n be count(n, d)

count(n, d) = ∑ (count(n-1, i)) where i varies from 0 to d

Total count = ∑ count(n-1, d) where d varies from 0 to n-1

Another method is based on below direct formula

Count of non-decreasing numbers with n digits =


N*(N+1)/2*(N+2)/3* ....*(N+n-1)/n
Where N = 10
Minimum Initial Points to Reach Destination
Given a grid with each cell consisting of positive, negative or no points i.e, zero points. We can move across a
cell only if we have positive points ( > 0 ). Whenever we pass through a cell, points in that cell are added to
our overall points. We need to find minimum initial points to reach cell (m-1, n-1) from (0, 0).

Constraints :

▪ From a cell (i, j) we can move to (i+1, j) or (i, j+1).


▪ We cannot move from (i, j) if your overall points at (i, j) is <= 0.
▪ We have to reach at (n-1, m-1) with minimum positive points i.e., > 0.

▪ Example:

Input: points[m][n] = { {-2, -3, 3},

▪ {-5, -10, 1},


▪ {10, 30, -5}
▪ };
▪ Output: 7
▪ Explanation:
▪ 7 is the minimum value to reach destination with
▪ positive throughout the path. Below is the path.

▪ (0,0) -> (0,1) -> (0,2) -> (1, 2) -> (2, 2)

▪ We start from (0, 0) with 7, we reach(0, 1)
▪ with 5, (0, 2) with 2, (1, 2) with 5, (2, 2)
▪ with and finally we have 1 point (we needed
▪ greater than 0 points at the end). 


▪ At the first look, this problem looks similar Max/Min Cost Path, but maximum overall points gained will
not guarantee the minimum initial points. Also, it is compulsory in the current problem that the points
never drops to zero or below. For instance, Suppose following two paths exists from source to
destination cell.

We can solve this problem through bottom-up table filling dynamic programing technique.

▪ To begin with, we should maintain a 2D array dp of the same size as the grid, where dp[i][j]
represents the minimum points that guarantees the continuation of the journey to destination
before entering the cell (i, j). It’s but obvious that dp[0][0] is our final solution. Hence, for this
problem, we need to fill the table from the bottom right corner to left top.
▪ Now, let us decide minimum points needed to leave cell (i, j) (remember we are moving from
bottom to up). There are only two paths to choose: (i+1, j) and (i, j+1). Of course we will choose the
cell that the player can finish the rest of his journey with a smaller initial points. Therefore we
have: min_Points_on_exit = min(dp[i+1][j], dp[i][j+1])
▪ Now we know how to compute min_Points_on_exit, but we need to fill the table dp[][] to get the solution
in dp[0][0].

How to compute dp[i][j]?

     The value of dp[i][j] can be written as below.


dp[i][j] = max(min_Points_on_exit – points[i][j], 1)


Let us see how above expression covers all cases.

▪ If points[i][j] == 0, then nothing is gained in this cell; the player can leave the cell with the same
points as he enters the room with, i.e. dp[i][j] = min_Points_on_exit.
▪ If dp[i][j] < 0, then the player must have points greater than min_Points_on_exit before entering (i,
j) in order to compensate for the points lost in this cell. The minimum amount of compensation is " -
points[i][j] ", so we have dp[i][j] = min_Points_on_exit - points[i][j].
▪ If dp[i][j] > 0, then the player could enter (i, j) with points as little as min_Points_on_exit – points[i]
[j]. since he could gain “points[i][j]” points in this cell. However, the value of min_Points_on_exit –
points[i][j] might drop to 0 or below in this situation. When this happens, we must clip the value to 1
in order to make sure dp[i][j] stays positive:

dp[i][j] = max(min_Points_on_exit – points[i][j], 1).
▪ Finally return dp[0][0] which is our answer.


dp[m-1][n-1] = points[m-1][n-1] > 0? 1:


Math.abs(points[m-1][n-1]) + 1;

Count of n digit numbers whose sum of digits equals to


given sum
Given two integers ‘n’ and ‘sum’, find count of all n digit numbers with sum of digits as ‘sum’. Leading 0’s are
not counted as digits.

1 <= n <= 100 and 1 <= sum <= 500

The idea is simple, we subtract all values from 0 to 9 from given sum and recur for sum minus that digit.
Below is recursive formula.

countRec(n, sum) = ∑countRec(n-1, sum-x)


where 0 =< x <= 9 and
sum-x >= 0
One important observation is, leading 0's must be
handled explicitly as they are not counted as digits.
So our final count can be written as below.
finalCount(n, sum) = ∑countRec(n-1, sum-x)
where 1 =< x <= 9 and
sum-x >= 0
static int countRec(int n, int sum)
{
// Base case
if (n == 0)
return sum == 0 ?1:0;

// Initialize answer
int ans = 0;

// Traverse through every digit and count


// numbers beginning with it using recursion
for (int i=0; i<=9; i++)
if (sum-i >= 0)
ans += countRec(n-1, sum-i);

return ans;
}

// This is mainly a wrapper over countRec. It


// explicitly handles leading digit and calls
// countRec() for remaining digits.
static int finalCount(int n, int sum)
{
// Initialize final answer
int ans = 0;

// Traverse through every digit from 1 to


// 9 and count numbers beginning with it
for (int i = 1; i <= 9; i++)
if (sum-i >= 0)
ans += countRec(n-1, sum-i);
return ans;
}

Count total number of N digit numbers such that the


difference between sum of even and odd digits is 1
Given a number n, we need to count total number of n digit numbers such that the sum of even digits is 1
more than the sum of odd digits. Here even and odd means positions of digits are like array indexes, for
exampl, the leftmost (or leading) digit is considered as even digit, next to leftmost is considered as odd and so
on.

This problem is mainly an extension of Count of n digit numbers whose sum of digits equals to given sum.
Here the solution of subproblems depend on four variables: digits, esum (current even sum), osum (current
odd sum), isEven(A flag to indicate whether current digit is even or odd).

Count ways to assign unique cap to every person


There 100 different types of caps each having a unique id from 1 to 100. Also, there ‘n’ persons each having a
collection of variable number of caps. One day all of these persons decide to go in a party wearing a cap but
to look unique they decided that none them will wear the same type of cap. So, count the total number of
arrangements or ways such that none of them is wearing same type of cap.

Constraints: 1 <= n <= 10 Since, number of ways could be large, so output modulo 1000000007
What is Bitmasking?
Suppose we have a collection of elements which are numbered from 1 to N. If we want to represent a subset
of this set than it can be encoded by a sequence of N bits (we usually call this sequence a “mask”). In our
chosen subset the i-th element belongs to it if and only if the i-th bit of the mask I set i.e., it equals to 1. For
example, the mask 10000101 means that the subset of the set [1… 8] consists of elements 1, 3 and 8. We
know that for a set of N elements there are total 2N subsets thus 2N masks are possible, one representing
each subset. Each mask is in fact an integer number written in binary notation.

The idea is to use the fact that there are upto 10 persons. So we can use a integer variable as a bitmask to
store which person is wearing cap and which is not.

Let i be the current cap number (caps from 1 to i-1 are already
processed). Let integer variable mask indicates the the persons w
earing and not wearing caps. If i'th bit is set in mask, then
i'th person is wearing a cap, else not.

// consider the case when ith cap is not included


// in the arrangement
countWays(mask, i) = countWays(mask, i+1) +

// when ith cap is included in the arrangement


// so, assign this cap to all possible persons
// one by one and recur for remaining persons.
∑ countWays(mask | (1 << j), i+1)
for every person j that can wear cap i
Note that the expression "mask | (1 << j)" sets j'th bit in mask.
And a person can wear cap i if it is there in the person's cap list
provided as input.

Since we want to access all persons that can wear a given cap, we use an array of vectors, capList[101]. A
value capList[i] indicates the list of persons that can wear cap i.

static long countWaysUtil(int mask, int i)


{
// If all persons are wearing a cap so we
// are done and this is one way so return 1
if (mask == allmask) return 1;

// If not everyone is wearing a cap and also there are no more


// caps left to process, so there is no way, thus return 0;
if (i > 100) return 0;

// If we already have solved this subproblem, return the answer.


if (dp[mask][i] != -1) return dp[mask][i];

// Ways, when we don't include this cap in our arrangement


// or solution set.
long ways = countWaysUtil(mask, i+1);

// size is the total number of persons having cap with id i.


int size = capList[i].size();

// So, assign one by one ith cap to all the possible persons
// and recur for remaining caps.
for (int j = 0; j < size; j++)
{
// if person capList[i][j] is already wearing a cap so continue as
// we cannot assign him this cap
if ((mask & (1 << capList[i].get(j))) != 0) continue;

// Else assign him this cap and recur for remaining caps with
// new updated mask vector
else ways += countWaysUtil(mask | (1 << capList[i].get(j)), i+1);
ways %= MOD;
}

// Save the result and return it.


return dp[mask][i] = (int) ways;
}

System.out.println(countWaysUtil(0, 1));

Longest Repeating Subsequence


Given a string, find length of the longest repeating subseequence such that the two subsequence don’t have
same string character at same position, i.e., any i’th character in the two subsequences shouldn’t have the
same index in the original string.

This problem is just the modification of Longest Common Subsequence problem. The idea is to find
theLCS(str, str)where str is the input string with the restriction that when both the characters are
same, they shouldn’t be on the same index in the two strings.
if (str.charAt(i-1) == str.charAt(j-1) && i!=j)
dp[i][j] = 1 + dp[i-1][j-1];
// If characters do not match
else
dp[i][j] = Math.max(dp[i][j-1], dp[i-1][j]);

Find the longest path in a matrix with given constraints


Given a n*n matrix where all numbers are distinct, find the maximum length path (starting from any cell) such
that all cells along the path are in increasing order with a difference of 1.

We can move in 4 directions from a given cell (i, j), i.e., we can move to (i+1, j) or (i, j+1) or (i-1, j) or (i, j-1)
with the condition that the adjacent cells have a difference of 1.

The idea is simple, we calculate longest path beginning with every cell. Once we have computed longest for
all cells, we return maximum of all longest paths. One important observation in this approach is many
overlapping subproblems. Therefore this problem can be optimally solved using Dynamic Programming.

Below is Dynamic Programming based implementation that uses a lookup table dp[][] to check if a problem is
already solved or not.

Time complexity of the above solution is O(n2). It may seem more at first look. If we take a closer look, we can
notice that all values of dp[i][j] are computed only once.

static int findLongestFromACell(int i, int j, int mat[][], int dp[][])


{
// Base case
if (i<0 || i>=n || j<0 || j>=n)
return 0;

// If this subproblem is already solved


if (dp[i][j] != -1)
return dp[i][j];

// Since all numbers are unique and in range from 1 to n*n,


// there is atmost one possible direction from any cell
if (j<n-1 && ((mat[i][j] +1) == mat[i][j+1]))
return dp[i][j] = 1 + findLongestFromACell(i,j+1,mat,dp);

if (j>0 && (mat[i][j] +1 == mat[i][j-1]))


return dp[i][j] = 1 + findLongestFromACell(i,j-1,mat,dp);

if (i>0 && (mat[i][j] +1 == mat[i-1][j]))


return dp[i][j] = 1 + findLongestFromACell(i-1,j,mat,dp);

if (i<n-1 && (mat[i][j] +1 == mat[i+1][j]))


return dp[i][j] = 1 + findLongestFromACell(i+1,j,mat,dp);

// If none of the adjacent fours is one greater


return dp[i][j] = 1;
}

// Function that returns length of the longest path


// beginning with any cell
static int finLongestOverAll(int mat[][])
{
// Initialize result
int result = 1;

// Create a lookup table and fill all entries in it as -1


int[][] dp = new int[n][n];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
dp[i][j] = -1;

// Compute longest path beginning from all cells


for (int i=0; i<n; i++)
{
for (int j=0; j<n; j++)
{
if (dp[i][j] == -1)
findLongestFromACell(i, j, mat, dp);

// Update result if needed


result = Math.max(result, dp[i][j]);
}
}

return result;
}

Number of paths with exactly k coins


Given a matrix where every cell has some number of coins. Count number of ways to reach bottom right from
top left with exactly k coins. We can move to (i+1, j) and (i, j+1) from a cell (i, j).

pathCount(m, n, k): Number of paths to reach mat[m][n] from mat[0][0]


with exactly k coins
If (m == 0 and n == 0)
return 1 if mat[0][0] == k else return 0
Else:
pathCount(m, n, k) = pathCount(m-1, n, k - mat[m][n]) +
pathCount(m, n-1, k - mat[m][n])

dp[m][n][k] = pathCountDPRecDP(mat, m-1, n, k-mat[m][n]) +


pathCountDPRecDP(mat, m, n-1, k-mat[m][n]);

Time complexity of this solution is O(m*n*k).

Collect maximum coins before hitting a dead end


Given a character matrix where every cell has one of the following values.

'C' --> This cell has coin

'#' --> This cell is a blocking cell.


We can not go anywhere from this.

'E' --> This cell is empty. We don't get


a coin, but we can move from here.
Initial position is cell (0, 0) and initial direction is right.

Following are rules for movements across cells.

If face is Right, then we can move to below cells


1. Move one step ahead, i.e., cell (i, j+1) and direction remains right.
2. Move one step down and face left, i.e., cell (i+1, j) and direction becomes left.
3. If face is Left, then we can move to below cells

1. Move one step ahead, i.e., cell (i, j-1) and direction remains left.
2. Move one step down and face right, i.e., cell (i+1, j) and direction becomes right.
4. Final position can be anywhere and final direction can also be anything. The target is to collect
maximum coins.


maxCoins(i, j, d): Maximum number of coins that can be


collected if we begin at cell (i, j)
and direction d.
d can be either 0 (left) or 1 (right)

// If this is a blocking cell, return 0. isValid() checks


// if i and j are valid row and column indexes.
If (arr[i][j] == '#' or isValid(i, j) == false)
return 0

// Initialize result
If (arr[i][j] == 'C')
result = 1;
Else
result = 0;

If (d == 0) // Left direction
return result + max(maxCoins(i+1, j, 1), // Down
maxCoins(i, j-1, 0)); // Ahead in left

If (d == 1) // Right direction
return result + max(maxCoins(i+1, j, 1), // Down
maxCoins(i, j+1, 0)); // Ahead in right

// Create lookup table and initialize all values as -1


int dp[R][C][2];
memset(dp, -1, sizeof dp);

// As per the question initial cell is (0, 0) and direction


// is right
return maxCoinsUtil(arr, 0, 0, 1, dp);

1. Time Complexity of above solution is O(R x C x d). Since d is 2, time complexity can be written as O(R x
C).


Count number of paths with at-most k turns


Given a “m x n” matrix, count number of paths to reach bottom right from top left with maximum k turns
allowed.
What is a turn? A movement is considered turn, if we were moving along row and now move along column.
OR we were moving along column and now move along row.

countPaths(i, j, k): Count of paths to reach (i,j) from (0, 0)


countPathsDir(i, j, k, 0): Count of paths if we reach (i, j)
along row.
countPathsDir(i, j, k, 1): Count of paths if we reach (i, j)
along column.
The fourth parameter in countPathsDir() indicates direction.

Value of countPaths() can be written as:


countPaths(i, j, k) = countPathsDir(i, j, k, 0) +
countPathsDir(i, j, k, 1)

And value of countPathsDir() can be recursively defined as:

// Base cases

// If current direction is along row


If (d == 0)
// Count paths for two cases
// 1) We reach here through previous row.
// 2) We reach here through previous column, so number of
// turns k reduce by 1.
countPathsDir(i, j, k, d) = countPathsUtil(i, j-1, k, d) +
countPathsUtil(i-1, j, k-1, !d);
// If current direction is along column
Else
// Similar to above
countPathsDir(i, j, k, d) = countPathsUtil(i-1, j, k, d) +
countPathsUtil(i, j-1, k-1, !d);
We can solve this problem in Polynomial Time using Dynamic Programming. The idea is to use a 4
dimensional table dp[m][n][k][d] where m is number of rows, n is number of columns, k is number of allowed
turns and d is direction.
int dp[MAX][MAX][MAX][2];
Time complexity of above solution is O(m*n*k)

You might also like