Dynamic Programming: "Those Who Cannot Remember The Past Are Condemned To Repeat It."
Dynamic Programming: "Those Who Cannot Remember The Past Are Condemned To Repeat It."
9 Dynamic Programming
“Those who cannot remember the past are condemned to repeat it.”
Dynamic Programing is all about remembering answers to the sub-problems you’ve already solved
and not solving it again.
Example
Writes down “1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 =” on a sheet of paper.
“What’s that equal to?”
Counting “Eight!”
Writes down another “1+” on the left.
“What about that?”
“Nine!” “ How’d you know it was nine so fast?”
“You just added one more!”
“So you didn’t need to recount because you remembered the re were eight! Dynamic Programming
is just a fancy way to say remembering stuff to save time later!”
175
Dynamic Programming
The intuition behind dynamic programming is that we trade space for time, i.e. to say that instead of
calculating all the states taking a lot of time but no space, we take up space to store the results of all
the sub-problems to save time later.
Let’s try to understand this by taking an example of Fibonacci numbers.
Fibonacci (n) = 1; if n = 0
Fibonacci (n) = 1; if n = 1
Fibonacci (n) = Fibonacci(n-1) + Fibonacci(n-2)
So, the first few numbers in this series will be: 1, 1, 2, 3, 5, 8, 13, 21, 35...
A code for it using pure recursion:
int fib (int n) {
if (n < 2)
return 1;
return fib(n-1) + fib(n-2);
}
Let’s Visualize :
Here we are running fibonacci function multiple times for the same value of n, which can be prevented
using memoization.
176
Dynamic Programming
Optimization Problems :
Dynamic Programming is typically applied to optimization problems. In such problems there can be
any possible solutions. Each solution has a value, and we wish to find a solution with the optimal
(minimum or maximum) value. We call such a solution an optimal solution to the problem.
MINIMUM STEPS TO ONE
http://www.spoj.com/problems/MST1/
On a positive integer, you can perform any one of the following 3 steps.
1. Subtract 1 from it.
2. If its divisible by 2, divide by 2.
3. If its divisible by 3, divide by 3. Now the question is, given a positive integer n, find the minimum
number of steps that takes n to 1.
Cannot apply Greedy! (Why ?)
Recursive Solution :
We will define the recurrence relation as
solve(n):
if n = 1
ans = 0
if n > 1
ans = min{1 + solve(n-1), 1 + solve(n/2), 1 + solve(n/3)}
Code :
int minStep(int n){
if(n == 1)
return 0;
int subOne = INF, divTwo = INF, divThree = INF;
//If number is greater than or equal to 2, subtract one
if(n >= 2)
subOne = 1 + minStep(n-1);
//If divsible by 2, divide by 2
if(n % 2 == 0)
divTwo = 1 + minStep(n/2);
//If divisible by 3, divide by 3
if(n%3 == 0)
divThree = 1 + minStep(n/3);
//Return the most optimal answer
return min(subOne, min(divTwo, divThree));}
177
Dynamic Programming
Can we do better?
The recursion tree for the above solution results in subproblem overlapping. So, we can improve the
solution using memoization.
Example :
I will be an amazing coder. How?
I will work hard like crazy. How?
I’ll practice more and try to improve. How?
I’ll start taking part in contests. How?
I’ll practicing. How?
I’m going to learn programming.
Code :
int minStep(int n){
//if already calculated, return this answer
if(dp[n] != -1)
return dp[n];
// Base case
if(n <= 1)
return 0;
int subOne = INF, divTwo = INF, divThree = INF;
//If number is greater than or equal to 2, subtract one
if(n >= 2)
subOne = 1 + minStep(n-1);
//If divsible by 2, divide by 2
if(n % 2 == 0)
divTwo = 1 + minStep(n/2);
//If divisible by 3, divide by 3
if(n%3 == 0)
divThree = 1 + minStep(n/3);
//Return the most optimal answer
return dp[n] = min(subOne, min(divTwo, divThree));
}
178
Dynamic Programming
2. Bottom Up DP : In Bottom Up, you start with the small solutions and then use these small
solutions to build up larger solutions.
Example :
I’m going to learn programming.
Then, I will start practicing.
Then, I will start taking part in contests.
Then, I’ll practice even more and try to improve.
After working hard like crazy,
I’ll be an amazing coder.
Code :
void minStep(){
//Base case
dp[1] = 0;
dp[0] = 0;
//Iterate for all possible numbers starting from 2
for(int i = 2;i<=2 * 10000000;i++){
dp[i] = 1 + dp[i-1];
if(i % 2 == 0)
dp[i] = min(dp[i],1 + dp[i/2]);
if(i % 3 == 0)
dp[i] = min(dp[i],1 + dp[i/3]);
}
return;
Example :
Input: coins[] = {25, 10, 5}, N = 30
Output: Minimum 2 coins required
We can use one coin of 25 cents and one of 5 cents
Input: coins[] = {9, 6, 5, 1}, N = 13
Output: Minimum 3 coins required
We can use one coin of 6 + 6 + 1 cents coins.
179
Dynamic Programming
Recursive Solution :
Start the solution with initially sum = N cents and at each iteration find the minimum coins required
by dividing the problem in subproblems where we take {C1, C2, ..., CM} coin and decrease the sum N
by C[i] (depending on the coin we took). Whenever N becomes 0, this means we have a possible
solution. To find the optimal answer, we return the minimum of all answer for which N became 0.
If N == 0, then 0 coins required.
If N > 0
minCoins(N , coins[0..m-1]) = min {1 + minCoins(N-coin[i] ,coins[0....m-1])}
where i varies from 0 to m-1 and coin[i] <= N
Code :
int minCoins(int coins[], int m, int N)
{
// base case
if (N == 0)
return 0;
// Initialize result
int res = INT_MAX;
// Try every coin that has smaller value than V
for (int i=0; i<m; i++)
{
if (coins[i] <= N)
{
int sub_res = 1 + minCoins(coins, m, N-coins[i]);
// see if result can minimized
if (sub_res < res)
res = sub_res;
}
}
return res;
}
180
Dynamic Programming
181
Dynamic Programming
2. Bottom Up DP : ith state of dp : dp[i] : Minimum number of coins required to sum to i cents.
Code :
int minCoins(int N, int M)
{
//Initializing all values to infinity i.e. minimum coins to make any
//amount of sum is infinite
for(int i = 0;i<=N;i++)
dp[i] = INF;
//Base case i.e. minimum coins to make sum = 0 cents is 0
dp[0] = 0;
//Iterating in the outer loop for possible values of sum between 1 to N
//Since our final solution for sum = N might depend upon any of these
values
for(int i = 1;i<=N;i++)
{
//Inner loop denotes the index of coin array.
//For each value of sum, to obtain the optimal solution.
for(int j = 0;j<M;j++)
{
//i —> sum
//j —> next coin index
//If we can include this coin in our solution
if(coins[j] <= i)
{
//Solution might include the newly included coin.
dp[i] = min(dp[i], 1 + dp[i - coins[j]]);
}
}
}
//for(int i = 1;i<=N;i++)cout<<i<<“ “<<dp[i]<<endl;
return dp[N];
}
T = O(N*M)
182
Dynamic Programming
Wrong Solution!
Greedy Approach :
Every year, sell the cheaper of the two(leftmost and right most) available wines.
Let the prices of 4 wines are: 2, 3, 5, 1, 4
At t = 1 year: {2,3,5,1,4} sell p1 = 2 to get cost = 2
At t = 2 years: {3,5,1,4} sell p2 = 3 to get cost = 2 + 2*3 = 8
At t = 3 years: {5,1,4} sell p5 = 4 to get cost = 8 + 4*3 = 20
At t = 4 years: {5,1} sell p4 = 1 to get cost = 20 + 1*4 = 24
At t = 5 years: {5} sell p3 = 5 to get cost = 24 + 5*5 = 49
Greedy approach gives an optimal answer of 49, but if we sell in the order of p1, p5, p4, p2, p3 for a
total profit 2 * 1 + 4 * 2 + 1 * 3 + 3 * 4 + 5 * 5 = 50, greedy fails.
Recursive Solution : Here we will try out all the possible options and output the optimal Answer.
if(start > end)
return 0
if(start <= end)
return maxPrice(price, start, end, year) = max{price[
start] * year + maxPrice(price, start+1, end, year + 1),
price[end] * year + maxPrice(price, start, end + 1,year + 1)},
183
Dynamic Programming
For each endpoint at every function call, we are doing the following:
Either we take this end point in the solution, increment/decrement the end point index.
Or we do not take this end point in the solution.
Since we are doing this for each and every n endpoints (n is number of wines on the table).
So, T = O(2^n) which is exponential time complexity.
Code :
int maxPrice(int price[], int start, int end, int year)
{
//Base case, stop when star of array becomes more than end.
if(start > end)
return 0;
/Including the wine with starting index in our solution
int incStart = price[start] * year + maxPrice(price,start + 1, end, year + 1);
//Including the wine with ending index in our solution
int incEnd = price[end] * year + maxPrice(price, start, end-1, year + 1);
//return the most optimal answer
return max(incStart, incEnd);
}
Can we do better?
Yes we can! By carefully observing the recursion tree, we can clearly see that we encounter the
property of subproblem overlapping which can be prevented using memoization or dynamic
programming.
184
Dynamic Programming
We can also solve this problem using the bottom up dynamic programming approach. Here we will
need to create a 2-Dimensional array for memoization where the states i and j in dp[i][j] denotes the
optimal answer between the starting index i and ending index j.
According to the definition of states, we define:
dp[i][j] = max{current_year * price[i] + dp[i+ 1][j], curr
ent_year * price[j] + dp[i][j+1]}
Bottom Up DP Code :
int maxPrice(int start,int end,int N){
//Initialize the dp array
for(int i = 0;i<N;i++)
{
for(int j = 0;j<N;j++)
dp[i][j] = 0;
}
//Outer loop denotes the starting index for our solution
for(int i = N-1;i>=0;i—)
{
//Inner loop denotes the ending index
for(int j = 0;j<N;j++)
{
//if (start > end), return 0
if(i > j)
dp[i][j] = 0;
else
{
//find the current year
int year = N - (j - i);
185
Dynamic Programming
Code :
MinCost(i,j) = min{ MinCost(i-1,j), MinCost(i,j-1) } + cost[i][j]
We now compute the values of the base cases: the topmost row and the leftmost column. For the
topmost row, a cell can be reached only from the cell on the left of it. Assuming zero-based index,
MinCost(0,j) = MinCost(0,j-1) + Cost[0][j]
MinCost(i,0) = MinCost(i-1,0) + Cost[i][0]
i.e. cost of reaching cell (0,j) = Cost of reaching cell (0,j-1) + Cost of visiting cell (0,j) Similarly,cost of
reaching cell (i,0) = Cost of reaching cell (i-1,0) + Cost of visiting cell (i,0).
186
Dynamic Programming
Code :
int MinCost(int Ro, int Col)
{
//This bottom-up approach ensures that all the sub-problems needed
// have already been calculated.
for(int i = 0;i<Ro;i++)
{
for(int j = 0;j<Col;j++)
{
//Base Cases
if(i == 0 && j == 0)
dp[i][j] = cost[0][0];
else if(i == 0)
dp[i][j] = dp[0][j-1] + cost[0][j];
else if(j == 0)
dp[i][j] = dp[i-1][0] + cost[i][0];
//Calculate cost of visiting (i,j) using the
//recurrence relation discussed above
else
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + cost[i][j];
}
}
//Return the optimum cost of reaching the last cell
return dp[Ro-1][Col-1];
}
Finding the number of ways to reach from a starting position to an ending position travelling in
specified directions only.
Problem Statement : Given a 2-D matrix with M rows and N columns, find the number of ways to
reach cell with coordinates (i,j) from starting cell (0,0) under the condition that you can only travel
one step right or one step down.
This problem is very similar to the previous one. To reach a cell (i,j), one must first reach either the cell
(i-1,j) or the cell (i,j-1) and then move one step down or to the right respectively to reach cell (i,j).
After convincing yourself that this problem indeed satisfies the optimal sub-structure and overlapping
subproblems properties, we try to formulate a bottom-up dynamic programming solution.
187
Dynamic Programming
Let NumWays(i,j) be the number of ways to reach position (i,j). As stated above, number of ways to
reach cell (i,j) will be equal to the sum of number of ways of reaching (i-1,j) and number of ways of
reaching (i,j-1). Thus, we have our recurrence relation as :
numWays(i,j) = numWays(i-1,j) + numWays(i,j-1)
Code :
Ques. Finding the number of ways to reach a particular position in a grid from a starting position
(given some cells which are blocked)
Problem Statement : Input is three integers M, N and P denoting the number of rows, number of
columns and number of blocked cells respectively. In the next P lines, each line has exactly 2 integers
i and j denoting that the cell (i, j) is blocked.
188
Dynamic Programming
https://www.codechef.com/problems/CD1IT4
The code below explains how to proceed with the solution. The problem is same as the previous one,
except for few extra checks (due to blocked cells).
int numWays(int Ro, int Col)
{
//if the initial block is blocked, we cannot move further
if(dp[0][0] == -1)
{
return 0;
}
//Number of ways for the first row
for(int j = 0;j<Col;j++)
{
//If any cell is blocked in the first row, we can not visit any
//cell that are to the right of this blcked cell
if(dp[0][j] == -1)
break;
//There is only one way to reach this cell i.e. from left cell
dp[0][j] = 1;
}
//Number of ways for first column
for(int i = 0;i<Ro;i++)
{
//Similar to first row
if(dp[i][0] == -1)
break;
dp[i][0] = 1;
}
//This bottom-up approach ensures that all the sub-problems needed
//have already been calculated.
for(int i = 1;i<Ro;i++)
{
for(int j = 1;j<Col;j++)
{
//If we encounter a blocked cell, do nothing
189
Dynamic Programming
if(dp[i][j] == -1)
continue;
//Calculate no. of ways to visit (i,j)
dp[i][j] = 0;
//If the cell on the left is not blocked, we can reach
//(i,j) from (i,j-1)
if(dp[i][j-1] != -1)
dp[i][j] = dp[i][j-1] % MOD;
//If the cell above is not blocked, we can reach
//(i,j) from (i-1,j)
if(dp[i-1][j] != -1)
dp[i][j] = (dp[i][j] + dp[i-1][j]) % MOD;
}
}
//If last cell is blocked, return 0
if(dp[Ro-1][Col-1] == -1)
return 0;
//Return the optimum cost of number of ways
return dp[Ro-1][Col-1];
}
Solution : http://pastebin.com/LeAhedeQ
Another Variant :
Problem Statement : You are given a 2-D matrix A of n rows and m columns where A[i][j] denotes the
calories burnt. Two persons, a boy and a girl, start from two corners of this matrix. The boy starts from
cell (1,1) and needs to reach cell (n,m). On the other hand, the girl starts from cell (n,1) and needs to
reach (1,m). The boy can move right and down. The girl can move right and up. As they visit a cell,
the amount in the cell A[i][j] is added to their total of calories burnt. You have to maximize the sum of
total calories burnt by both of them under the condition that they shall meet only in one cell and the
cost of this cell shall not be included in either of their total.
http://codeforces.com/contest/429/problem/B
Let us analyse this problem in steps:
The boy can meet the girl in only one cell.
So, let us assume they meet at cell (i,j).
190
Dynamic Programming
Boy can come in from left or the top, i.e. (i,j-1) or (i-1,j). Now he can move right or down.That is,
the sequence for the boy can be:
(i,j-1)—>(i,j)—>(i,j+1)
(i,j-1)—>(i,j)—>(i+1,1)
(i-1,j)—>(i,j)—>(i,j+1)
(i-1,j)—>(i,j)—>(i+1,j)
Similarly, the girl can come in from the left or bottom, i.e. (i,j-1) or (i+1,j) and she can go up or right.
The sequence for girl’s movement can be:
(i,j-1)—>(i,j)—>(i,j+1)
(i,j-1)—>(i,j)—>(i-1,j)
(i+1,j)—>(i,j)—>(i,j+1)
(i+1,j)—>(i,j)—>(i-1,j)
Comparing the 4 sequences of the boy and the girl, the boy and girl meet only at one position (i,j), iff
Boy: (i,j-1)—>(i,j)—>(i,j+1) and Girl: (i+1,j)—>(i,j)—>(i-1,j)
or
Boy: (i-1,j)—>(i,j)—>(i+1,j) and Girl: (i,j-1)—>(i,j)—>(i,j+1)
191
Dynamic Programming
//Here girl_start[i][j] —> the max calories that can be burnt if the girl
//starts from (M,1) and goes up to (i,j)
for(int i = M;i>=1;i—)
{
for(int j = 1;j<=N;j++)
{
girl_start[i][j] = calorie[i][j] + max(girl_start[i+1][j], girl_start[i][j-1]);
}
}
//building boy_end[][] table in bottom up fashion which specifies the journey from end
to start
//Here boy_end[i][j] —> the max calories that can be burnt if the boy start
//from end i.e. (M,N) and comes back to (i,j)
for(int i = M;i>=1;i—)
{
for(int j = N;j>=1;j—)
{
boy_end[i][j] = calorie[i][j] + max(boy_end[i+1][j], boy_end[i][j+1]);
}
}
//building girl_end[][] table in bottom up fashion which specifies the journey from end
to start
//Here girl_end[i][j] —> the max calories that can be burnt if the girl start
//from end i.e. (1,N) and comes back to (i,j)
for(int i = 1;i<=M;i++)
{
for(int j = N;j>=1;j—)
{
girl_end[i][j] = calorie[i][j] + max(girl_end[i-1][j], girl_end[i][j+1]);
}
}
//Iterate over all the possible meeting points i.e. between (2,2) to (M-1,N-1)
//consider this point as the actual meeting point and calculate the max possible answer
int ans = 0;
for(int i = 2;i<M;i++)
{
for(int j = 2;j<N;j++)
{
int ans1 = boy_start[i][j-1]+boy_end[i][j+1]+girl_start[i+1][j] + girl_end[i-1][j];
192
Dynamic Programming
Solution: http://pastebin.com/cnBqeJEP
BUILDING BRIDGES
Problem Statement: Given two array of numbers which denotes the end points of bridges. What is
the maximum number of bridges that can be built if ith point of first array must be connected to ith
point of second array and two bridges cannot overlap each other.
http://www.spoj.com/problems/BRIDGE/
Recurrence relation :
L(i) = 1 + max{ L(j) } where 0 < j < i and arr[j] < arr[i];
or
Code :
int LIS(int n)
{
int i,j,res = 0;
/* Initialize LIS values for all indexes */
for (i = 0; i < n; i++ )
lis[i] = 1;
/* Compute optimized LIS values in bottom up manner */
for (i = 1; i < n; i++ )
for (j = 0; j < i; j++ )
if ( arr[i] > arr[j] && lis[i] < lis[j] + 1)
lis[i] = lis[j] + 1;
193
Dynamic Programming
T = O(n^2)
We can also print the Longest Increasing Subsequence as:
void print_lis(int n)
{
//denotes the current LIS
//initially it is equal to the LIS of the whole sequence
int cur_lis = LIS(n);
//denotes the previous element printed
//to print the LIS, previous element printed must always be larger than current
//element (if we are printing the LIS backwards)
//Initially set it to infinity
int prev = INF;
for(int i = n-1;i>=0;i—)
{
//find the element upto which the LIS equal to the cur_LIS
//and that element is less than the previous one printed
if(lis[i] == cur_lis && arr[i] <= prev)
{
cout<<arr[i]<<“ “;
cur_lis—;
prev = arr[i];
}
if(!cur_lis)
break;
}
return;
}
194
Dynamic Programming
In this problem we will use the concept of LIS. Two bridges will not cut each other if both their end points
are either in non-increasing or non-decreasing order. To find the solution we will first pair the endpoints
i.e. ith point in 1st sequence is paired with ith point in 2nd sequence. Then sort the points w.r.t 1st point in
the pair and apply LIS on second point of the pair.
Voila! You have a solution :)
Example: First consider the pairs: (2,6), (5, 4), (8, 1), (10, 2), sort it according to the first element of the pairs
(in this case are already sorted) and compute the lis on the second element of the pairs, thus compute the
LIS on 6 4 1 2, that is 1 2. Therefore the non overlapping bridges we are looking for are (8, 1) and (10, 2).
Code :
int maxBridges(int n)
{
//for a single point there is always a bridge
if(n == 1)
return 1;
//Sort the points according to the first sequence
sort(points.begin(), points.end());
//Apply LIS on the second sequence
int i,j,res = 0;
//Initialize LIS values for all indexes
for (i = 0; i < n; i++ )
lis[i] = 1;
//Compute optimized LIS values in bottom up manner
for (i = 1; i < n; i++ )
for (j = 0; j < i; j++ )
if ( points[i].second >= points[j].second && lis[i] < lis[j] + 1)
lis[i] = lis[j] + 1;
//Pick maximum of all LIS values
for (i = 0; i < n; i++ )
if (res < lis[i])
res = lis[i];
return res;
}
Solution:http://pastebin.com/UdHtgasV
195
Dynamic Programming
Recurrence Relation :
LCS(str1, str2, m, n) = 0, if m = 0 or n = 0 //Base Case
LCS(str1, str2, m, n) = 1 + LCS(str1, str2, m-1, n-1), if str1[m] = str2[n]
LCS(str1, str2, m, n) = max{LCS(str1, str2, m-1, n), LCS( str1, str2, m, n-1)}, otherwise
LCS can take value between 0 and min(m,n).
Code :
int lcs( char *str1, char *str2, int m, int n )
{
//Following steps build L[m+1][n+1] in bottom up fashion. Note
//that L[i][j] contains length of LCS of str1[0..i-1] and str2[0..j-1]
for (int i=0; i<=m; i++)
{
for (int j=0; j<=n; j++)
{
if (i == 0 || j == 0)
LCS[i][j] = 0;
else if (str1[i-1] == str2[j-1])
LCS[i][j] = LCS[i-1][j-1] + 1;
else
LCS[i][j] = max(LCS[i-1][j], LCS[i][j-1]);
}
}
//Print LCS
196
Dynamic Programming
T = O(mn)
197
Dynamic Programming
Approach:
First we will find the LCS of the two strings.
We will insert the non-LCS characters in the LCS found above in their original order.
198
Dynamic Programming
if (str1[i-1] == str2[j-1])
{
// Put current character in result
lcs_arr += str1[i-1];
// reduce values of i, j and index
i—;
j—;
index—;
}
// If not same, then find the larger of two and
// go in the direction of larger value
else if (LCS[i-1][j] > LCS[i][j-1])
i—;
else
j—;
}
//Print LCS
//cout<<lcs_arr<<endl;
//Use LCS to get the final answer
i = m-1, j = n-1;
//Use to make the final array
int l = 0,k = 0;
//contains the length of longest common subsequence of the two strings
index = LCS[m][n];
//string containing the final answer
string ans = “”;
while(i>=0 || j>=0)
{
//If we have not covered the full LCS array
if(l < index)
{
//Find the non-LCS charactes of A and put them in a array
199
Dynamic Programming
Solution: http://pastebin.com/EB9U8PNu
200
Dynamic Programming
EDIT DISTANCE
Problem Statement: Given two strings str1 and str2 and below operations can be performed on str1.
Find min number of edits(operations) required to convert str1 to str2.
Insert Remove Replace
All the above operations are of equal cost.
http://www.spoj.com/problems/EDIST/
Example :
str1 = “cat”
str2 = “cut”
Replace ‘a’ with ‘u’, min number of edits = 1
str1 = “sunday”
str2 = “saturday”
Last 3 characters are same, we only need to replace “un” with “atur”.
Replace n—>r and insert ‘a’ and ‘t’ before ‘u’, min number of edits = 3
Recurrence Relation :
if str1[m] = str2[n]
editDist(str1, str2, m, n) = editDist(str1, str2, m-1, n-1)
else
editDist(str1, str2, m, n) = 1 + min{editDist(str1, str2, m-1, n) //Remove
editDist(str1, str2, m, n-1) //Insert
editDist(str1, str2, m-1, n-1) //Replace
}
201
Dynamic Programming
Dynamic Programming :
int editDist(string str1, string str2){
int m = str1.length();
int n = str2.length();
// dp[i][j] —> the minimum number of edits to transform str1[0...i-1] to
str2[0...j-1]
//Fill up the dp table in bottom up fashion
for(int i = 0;i<=m;i++)
{
for(int j = 0;j<=n;j++)
{
//If both strings are empty
if(i == 0 && j == 0)
dp[i][j] = 0;
//If first string is empty, only option is to
//insert all characters of second string
//So number of edits is the length of secondstring
else if(i == 0)
dp[i][j] = j;
//If second string is empty, only option is to
//remove all characters of first string
//So number of edits is the length of first string
else if(j == 0)
dp[i][j] = i;
//If the last character of the two strings are
//same, ignore this character and recur for the
//remaining string
else if(str1[i-1] == str2[j-1])
dp[i][j] = dp[i-1][j-1];
//If last character is different, we need at least one
//edit to make them same. Consider all the possibilities
//and find minimum
else
dp[i][j] = 1 + min(min(
dp[i-1][j], //Remove
202
Dynamic Programming
dp[i][j-1]), //Insert
dp[i-1][j-1] //Replace
);
}
}
//Return the most optimal solution
return dp[m][n];
}
Solution: http://pastebin.com/ZVqfmHHJ
MIXTURES
Problem Statement : Given n mixtures on a table, where each mixture has one of 100 different colors
(0 - 99). When mixing two mixtures of color ‘a’ and ‘b’, resulting mixture have the color (a + b) mod 100
and amount of smoke generated is a*b. Find the minimum amount of smoke that we can get when
mixing all mixtures together, given that we can only mix two adjacent mixtures.
http://www.spoj.com/problems/MIXTURES/
The first thing to notice here is that, if we mix mixtures i...j into a single mixture, irrespective of the
steps taken to achieve this, the final color of the mixture is same and equal to sum(i,j) = sum(color(i)..
color(j)) mod 100.
So we define dp(i,j) as the most optimum solution where least amount of smoke is produced while
mixing the mixtures from i...j into a single mixture. For achieving this, at the previous steps, we would
have had to combine the two mixtures which are resultants of ranges i...k and k+1...j where i <= k <= j.
So it’s about splitting the mixture into 2 subsets and each subset into 2 more subsets and so on such
that smoke produced is minimized. Hence the recurrence relation will be:
dp(i,j) = min(k: i <= k < j) {dp(i,k) + dp(k+1,j) + sum(i,k)*sum(k+1,j)}
Code :
int minSmoke(int n)
{
//Building the cummulative sum array
sum[0] = col[0];
for(int i = 1;i<n;i++)
sum[i] = (sum[i-1] + col[i]);
//dp[i][j] —> min smoke produced after mixing {color(i).....color(j)}
//Note color after mixing {color(i).....color(j)} is sum[i...j] mod M
//Building the dp in bottom up fashion
203
Dynamic Programming
for(int i = n-1;i>=0;i—)
{
for(int j = 0;j<n;j++)
{
//Base Case
//if i and j are equal then we have a single mixture and
//hence no smoke is produced
if(i == j)
{
dp[i][i] = 0;
continue;
}
dp[i][j] = LONG_MAX;
for(int k = i;k<j;k++)
{
int color_left = (sum[k] - sum[i-1]) % 100;
int color_right = (sum[j] - sum[k]) % 100;
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + (color_left *
color_right));
}
}
}
//return the most optimal answer
return dp[0][n-1];
}
204
Dynamic Programming
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 (excludingnth 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.
Recurrence Relation :
//Base case
//If we have explored all the items all we have reached the maximum capacity
of Knapsack
if (n=0 or W=0)
return 0
//If the weight of nth item is greater than the capacity of knapsack, we cannot
include //this item
if (wieght[n] > W)
return solve(n-1, W)
otherwise
return max{ solve(n-1, W), //We have not included the item
solve(n-1, W-weight[n]) //We have included the item in the knapsack
}
If we build the recursion tree for the above relation, we can clearly see that the property of
overlapping sub-problems is satisfied. So, we will try to solve it using dynamic programming.
Let us define the dp solution with states i and j as
dp[i,j] —> max value that can be obtained with objects u
pto index i and knapsack capacity of j.
The most optimal solution to the problem will be dp[N][W] i.e. max value that can be obtained upto
index N with max capacity of W.
Code :
int knapsack(int N, int W)
{
for(int i = 0;i<=N;i++)
{
for(int j = 0;j<=W;j++)
{
//Base case
205
Dynamic Programming
Can we do better?
If we observe carefully, we can see that the dp solution with states (i,j) will depend on state (i-1, j) or
(i-1, j-wt[i-1]). In either case the solution for state (i,j) will lie in the i-1th row of the memoization
table. So at every iteration of the index, we can copy the values of current row and use only this row
for building the solution in next iteration and no other row will be used. Hence, at any iteration we
will be using only a single row to build the solution for current row. Hence, we can reduce the space
complexity to just O(W).
206
Dynamic Programming
Space-Optimized DP Code :
int knapsack(int N, int W)
{
for(int j = 0;j<=W;j++)
dp[0][j] = 0;
for(int i = 0;i<=N;i++)
{
for(int j = 0;j<=W;j++)
{
//Base case
//When no object is to be explored or our knapsack’s capacity is 0
if(i == 0 || j == 0)
dp[1][j] = 0;
//When the wieght of the item to be considered is more than the
//knapsack’s capacity, we will not include this item
if(wt[i-1] > j)
dp[1][j] = dp[0][j];
else
dp[1][j] = max(
//If we include this item, we get a value of val[i-1] but the
//capacity of the knapsack gets reduced by the weight of that
//item.
val[i-1] + dp[0][j - wt[i-1]],
//If we do not include this item, max value will be the
//solution obtained by taking objects upto index i-1, capacity
//of knapsack will remain unchanged.
dp[0][j]);
}
//Here we are copying value of current row into the previous row,
//which will be used in building the solution for next iteration ofrow.
for(int j = 0;j<=W;j++)
dp[0][j] = dp[1][j];
}
return dp[1][W];
}
207
Dynamic Programming
Example :
Let the length of the rod is 6.
Price of size is given below
Approach : Here we have to generate all the configurations of different pieces and find the highest priced
configuration. We can get the best price(maximum revenue) by making a cut at different positions and
comparing the values obtained after a cut.
Code :
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int max(int a, int b)
{
return (a>b)?a:b;
}
208
Dynamic Programming
209
Dynamic Programming
Overlapping Subproblems :
n 1
k
when we sum up over all possibilities for 0 £ k £ n – 1 then
n 1 n 1
n 1 (n 1)!
k
k 0 k! * (n 1 k)!
2n1
k 0
210
Dynamic Programming
211
Dynamic Programming
{
return (a>b)?a:b;
}
int RodCut(int p[], int n)
{
int max_revenue = INT_MIN;
if(n<=0)
return 0;
else if(Revenue[n]==-1)
{
for(int i=1;i<=(n);i++)
{
max_revenue = max(max_revenue,p[i]+RodCut(p,n-i));
}
Revenue[n] = max_revenue;
return Revenue[n];
}
else
return Revenue[n];
}
int main()
{
/* code */
int t;
cin>>t;
for(int i=0;i<t;i++)
{
int n;
cin>>n;
int p[n+1];
p[0]=0;
for(int i=1;i<=n;i++)
{
cin>>p[i];
// Revenue[i]=p[i];
212
Dynamic Programming
}
for(int i=0;i<=n;i++)
Revenue[i] = -1;
Bottom Up :
#include <iostream>
#include <bits/stdc++.h>
#define MAX 200
using namespace std;
int max(int x, int y)
{
return (x>y)?x:y;
}
int RodCut(int p[], int n)
{
int Revenue[n+1];
Revenue[0]=0;
for(int i=1;i<=n;i++)
{
int best_price = INT_MIN;
for(int j=1;j<=i;j++)
{
best_price = max(best_price, p[j]+Revenue[i-j]);
}
Revenue[i]=best_price;
}
return Revenue[n];
}
int main()
213
Dynamic Programming
{
/* code */
int t;
cin>>t;
for(int i=0;i<t;i++)
{
int n;
cin>>n;
int p[n+1];
p[0]=0;
for(int i=1;i<=n;i++)
{
cin>>p[i];
}
cout<<“Maximum Revenue of given rod of length “<< n <<“ =
“<<RodCut(p,n)<<“\n”;
}
return 0;
}
i.e. X = A1A2A3 . . . . . . . . . . . An – 1 An
214
Dynamic Programming
Recursive Relation :
2 L[i 1, j 1], if X[i] X[j]
L[i, j]
max(L[i, j 1], m[i 1, j]), if X[i] [j]
Base Cases :
1, if i j
L[i, j]
2, if i j 1, X[i] X[j]
Start the code with i = 0 and j = n – 1;
Code :
#include <iostream>
#include <bits/stdc++.h>
#define MAX 200
using namespace std;
int SubSeq(char seq[], int i, int j)
{
int length_SubSeq =0;
if(i==j)
return 1;
else if(i==(j-1) && seq[i]==seq[j])
{
return 2;
}
else if(seq[i]==seq[j])
length_SubSeq = SubSeq(seq,i+1,j-1)+2;
else
length_SubSeq = max(SubSeq(seq,i+1,j), SubSeq(seq, i, j-1));
return length_SubSeq;
}
int main()
{
int t;
cin>>t;
for(int i=0;i<t;i++)
{
int n;
215
Dynamic Programming
cin>>n;
char seq[n];
for(int j=0;j<n;j++)
{
cin>>seq[j];
}
cout<<“Length of maximum palindrome Subsequence is “<<SubSeq(seq,0,n-
1)<<“\n”;
}
}
216
Dynamic Programming
length[i][j]=1;
else if(seq[i]==seq[j] && i==(j-1))
{
length[i][j]=2;
}
else if(seq[i]==seq[j])
length[i][j] = SubSeq(seq,i+1,j-1)+2;
else
length[i][j] = max(SubSeq(seq,i+1,j), SubSeq(seq, i, j-1));
}
return length[i][j];
}
int main()
{
int t;
cin>>t;
for(int i=0;i<t;i++)
{
int n;
cin>>n;
char seq[n];
for(int j=0;j<n;j++)
{
cin>>seq[j];
}
for(int j=0;j<n;j++)
{
for(int k=0;k<n;k++)
length[j][k]=-1;
}
cout<<“Length of maximum palindrome Subsequence is “<<SubSeq(seq,0,n-1)<<“\n”;
}
}
217
Dynamic Programming
Bottom Up (Tabulation) :
#include <iostream>
#include <bits/stdc++.h>
#define MAX 200
using namespace std;
int SubSeq(char seq[], int n)
{
int length[n][n];
for(int i=0;i<n;i++)
length[i][i]=1;
for(int k=2;k<=n;k++)
{
for(int i=0;i<=(n-k);i++)
{
int j = k+i-1;
if(k==2 && seq[i]==seq[j])
{
length[i][j]=2;
}
else
{
if(seq[i]==seq[j])
{
length[i][j] = 2+length[i+1][j-1];
}
else
{
length[i][j]=max(length[i][j-1],length[i+1][j]);
}
}
}
}
return length[0][n-1];
}
int main()
{
int t;
cin>>t;
for(int i=0;i<t;i++)
{
int n;
218
Dynamic Programming
cin>>n;
char seq[n];
for(int j=0;j<n;j++)
{
cin>>seq[j];
}
cout<<“Length of maximum palindrome Subsequence is “<<SubSeq(seq,n)<<“\n”;
}
}
Multiplication Cost :
Let the size of the matrix is given A1 40 20, A 2 20 30, A 3 30 10, A 4 10 30
Then multiplication cost for 1st multiplication would be :
(A1 , A 2 ) : Cost (40 20 30), Matrix Size 40 30
(A 3 , A 4 ) : Cost (30 10 30), Matrix Size 30 30
Total Cost (40 10 30) (30 10 30) (40 30 30) 70200 size of Resultant Matrix = 40 30
Total Cost = (40 * 20 * 30) + (30 * 10 * 30) + (40 * 30 * 30) = 70200 Size of Resultant Matrix = 40 * 30
Similarly cost for other multiplication would be given below :
219
Dynamic Programming
Approach :
Approach towards the solution is to place parentheses at all possible places, calculate the cost for each
placement and return the minimum value.
Suppose p[n + 1] is an array contain the size of each matrix. i.e.
size of Ai = p[i – 1] p[i], 1 d i d n
Let m[i,j] be the minimum number scalar multiplication needed to compute the multiplication of matrix
Ai to Aj (i.e. Ai . Aj + 1 . Ai + 2 . . . . . . . . . Aj).
So m[1,n] would be the lowest cost to compute the multiplication of matrix to (i.e. A1 . A2 . A3 . . . . . . An).
A recursive Solution :
We can split the product the product Ai . Ai + 1 . Ai + 2 . . . . . . . Aj between Ak and Ak + 1 where i k < j then m[i,j]
equals the minimum cost for computing the subproducts A i. . . . . . kand Ak + 1. . . . . . j, plus the cost of multiplying
these two matrices together.
Size of Ai. . . . . . k = p[i-1]*p[k] and size of Ak + 1. . . . . . j = p[k]*p[j]
then multiplication cost of Ai. . . . . . k Ak + 1. . . . . . j = p[i – 1] p[k] p[j]
Ai. . . . . . k = minimum cost to multiply the matrices (Ai . Ai + 1 . Ai + 2 . . . . . . . . . Ak) = m[i, k]
Ak + 1. . . . . . j = minimum cost to multiply the matrices= m[k + 1, j]
m[i, j] = m[i, k] + m[k + 1, j] + p[i – 1] p[k] p[j]
Code :
#include <iostream>
#include <bits/stdc++.h>
#define MAX 200
using namespace std;
int min(int x, int y)
{
return (x>y)?y:x;
}
220
Dynamic Programming
221
Dynamic Programming
Code :
#include <iostream>
#include <bits/stdc++.h>
#define MAX 200
using namespace std;
int mult[MAX][MAX];
int min(int x, int y)
{
return (x>y)?y:x;
}
int Matrix_Chain(int p[], int i,int j)
{
if(mult[i][j]!=-1)
return mult[i][j];
else
{
int mult_cost = INT_MAX;
if(i==j)
mult[i][j]=0;
else
{
for(int k=i;k<j;k++)
{
222
Dynamic Programming
mult_cost = min(mult_cost,
Matrix_Chain(p,i,k)+Matrix_Chain(p,k+1,j)+p[i-1]*p[k]*p[j]);
}
mult[i][j]=mult_cost;
}
return mult[i][j];
}
//return mult_cost;
}
int main()
{
int t;
cin>>t;
for(int i=0;i<t;i++)
{
int n;
cin>>n;
int p[n+1];
for(int j=0;j<=n;j++)
{
cin>>p[j];
}
for(int j=0;j<MAX;j++)
{
for(int k=0;k<MAX;k++)
{
mult[j][k]=-1;
}
}
cout<<“Minimum cost for matrix multiplication is :”<<“
“<<Matrix_Chain(p,1,n)<<“\n”;
}
}
223
Dynamic Programming
Bottom Up (Tabulation) :
#include <iostream>
#include <bits/stdc++.h>
#define MAX 200
using namespace std;
int min(int x, int y)
{
return (x>y)?y:x;
}
int Matrix_Chain(int p[], int n)
{
int mult_cost[n][n];
for(int i=1;i<n;i++)
{
mult_cost[i][i]=0;
}
for(int l=2;l<n;l++)
{
for(int i=1;i<(n-l+1);i++)
{
int j=i+l-1;
mult_cost[i][j] = INT_MAX;
for(int k=i;k<j;k++)
{
mult_cost[i][j] = min(mult_cost[i][j],
mult_cost[i][k]+mult_cost[k+1][j]+p[i-
1]*p[k]*p[j]);
}
}
}
return mult_cost[1][n-1];
}
int main()
{
int t;
224
Dynamic Programming
cin>>t;
for(int i=0;i<t;i++)
{
int n;
cin>>n;
int p[n+1];
for(int j=0;j<=n;j++)
{
cin>>p[j];
}
225
Dynamic Programming
DO IT YOURSELVES
226
Dynamic Programming
227
Dynamic Programming
228