DP
How to Identify a DP problem?
- Count the total number of ways
- Given multiple ways of doing a task, which way will give the minimum or the maximum output.
We can try to apply recursion. Once we get the recursive solution, we can go ahead to convert it to a dynamic programming one.
Steps To Solve The Problem After Identification
Once the problem has been identified, the following three steps comes handy in solving the problem:
- Try to represent the problem in terms of indexes.
- Try all possible choices/ways at every index according to the problem statement.
- If the question states
- Count all the ways – return sum of all choices/ways.
- Find maximum/minimum- return the choice/way with maximum/minimum output.
1D
Climbing stairs -
Problem Statement: Given a number of stairs. Starting from the 0th stair we need to climb to the “Nth” stair. At a time we can climb either one or two steps. We need to return the total number of distinct ways to reach from 0th to Nth stair.
- RECURSSION- TOP - DOWN APPROACH
int solve(int ind){
//base case
if(ind==0){
return 1 ; //ek hi stair h
}
return solve(ind-1)+ solve(ind-2);
}
int countways(int n){
return solve(ind);
}
- MEMOIZATION- top-down
STEPS-
1. if(dp[n]!= -1) return dp[n];// if previously computed
2. return the value from the dp array. // return dp[n]= f(n-1,dp) + f(n-2,dp);
3. vector<int> dp(n+1,-1); (declare the size of n+1 basically n is the changing parameter);
int solve(int ind, vector<int>&dp){
//base case
if(ind==0){
return 1 ; //ek hi stair h
}
if(dp[n]!= -1) return dp[n];
return dp[n]= solve(ind-1,dp)+ solve(ind-2,dp);
}
int countways(int n){
vector<int> dp(n,-1);
return solve(n,dp);
}
- Tabulation- Bottom up
int main() {
int n=3;
vector<int> dp(n+1,-1);
dp[0]= 1;
dp[1]= 1;
for(int i=2; i<=n; i++){
dp[i] = dp[i-1]+ dp[i-2];
}
cout<<dp[n];
return 0;
}
O(N)
Frog Jump - jump by 1 or 2 step . find min energy
- recurssion
int f(int ind, vector<int> &heights)
{
if (ind == 0)
return 0;
int left = f(ind - 1, heights) + abs(heights[ind] - heights[ind - 1]);
int right = INT_MAX; // NOT GOING TO HAPPEN ALWAYS
if (ind > 1)
right = f(ind - 2, heights) + abs(heights[ind] - heights[ind - 2]);
return min(left, right);
}
int frogJump(int n vector<int> &height)
{
return f(n - 1, heights); // n-1 = last index
}
- Memoization
int solve(int ind, vector<int>& height, vector<int>& dp){
if(ind==0) return 0;
if(dp[ind]!=-1) return dp[ind];
int jumpTwo = INT_MAX;
int jumpOne= solve(ind-1, height,dp)+ abs(height[ind]-height[ind-1]);
if(ind>1)
jumpTwo = solve(ind-2, height,dp)+ abs(height[ind]-height[ind-2]);
return dp[ind]=min(jumpOne, jumpTwo);
}
int main() {
vector<int> height{30,10,60 , 10 , 60 , 50};
int n=height.size();
vector<int> dp(n,-1);
cout<<solve(n-1,height,dp);
}
- Tabulation approach
- Declare a dp[] array of size n.
- First, initialize the base condition values, i.e dp[0] as 0.
- Set an iterative loop that traverses the array( from index 1 to n-1) and for every index calculate jumpOne and jumpTwo and set dp[i] = min(jumpOne, jumpTwo).
1. initialize dp[n]->0
2. base case likho
3. run for loop
4. look in recusrrsion by replacing the function with dp
5. store dp[ind]
int frogJump(int n vector<int> &height)
{
// step 1
vector<int> dp(n, 0); // dp vector and initialized it with 1
// step 2
// base case
dp[0] = 0;
// step 3
for (int i = 1; i < n; i++)
{
int left = dp[i - 1] + abs(heights[i] - heights[i - 1]);
int right = INT_MAX;
if (ind > 1)
right = dp[i - 2] + abs(heights[i] - heights[i - 2]);
// step 4
dp[i] = min(left, right);
}
return dp[n - 1;]
}
tc/sc- o(n)
**Maximum Sum of Non-Adjacent Elements | House Robber |→ return max sum of subsequnce none of the element must be adjancent**
- recusrrsion
int f(int ind, vector<int> &arr)
{
if (ind == 0)
return arr[ind];
if (ind < 0)
return 0;
int pick = arr[ind] + f(ind - 2, arr);
int nonPick = 0 + f(ind - 1, arr);
return max(pick, nonPick);
}
int maximumsum(vector<int> &nums)
{
int n = arr.size();
return f(n - 1, arr);
}
MEMOIZATION-
int f(int ind, vector<int> &arr, vector<int> &dp)
{
if (dp[ind] != -1)
return dp[ind];
if (ind == 0)
return arr[ind];
if (ind < 0)
return 0;
int pick = arr[ind] + f(ind - 2, arr, dp);
int nonPick = 0 + f(ind - 1, arr, dp);
return dp[ind] = max(pick, nonPick);
}
int maximumsum(vector<int> &nums)
{
int n = arr.size();
vector<int> dp(n, -1);
return f(n-1, arr,dp);
}
Tabulation
int f(int n, vector<int>& arr, vector<int>& dp){
vector<int> dp(n,-1);
dp[0]= arr[0];
for(int i=1 ;i<n; i++){
int pick = arr[i];
if(i>1)
pick += dp[i-2];
int nonPick = 0+ dp[i-1];
dp[i]= max(pick, nonPick);
}
return dp[n-1];
}
2d
**Ninja's Training- the same activity can’t be performed on two consecutive days. We need to find the maximum merit points the ninja can attain in N Days.**
f(ind, last day) - task performed on last day
- Recurssion
int f(int day , int last, vector<vector<int>>&points){
if(day==0){ // last day
int maxi=0;
//go across all task (there are 3 task)
for(int task=0;task<3; task++){
//this task is not done at last
if(task!=last){
maxi=max(maxi, poitns[0][task]);// day=0
}
}
return maxi;
}
//if that's not case
int maxi=0;
for(int task=0;task<3;task++){
if(task!=last){
int point =points [day][task]+ f(day-1, task , points);
maxi=max(maxi,points);
}
}
return maxi;
}
int ninja(int n , vector<vector<int>>&points){
return f(n-1,3,points);
}
memoization
int f(int day , int last, vector<vector<int>>&points, vector<vector<int>>&dp){
if (dp[day][last] != -1) return dp[day][last];
if(day==0){ // last day
int maxi=0;
//go across all task (there are 3 task)
for(int task=0;task<3; task++){
//this task is not done at last
if(task!=last){
maxi=max(maxi, poitns[0][task]);// day=0
}
}
return dp[day][last] = maxi;
}
//if that's not case
int maxi=0;
for(int task=0;task<3;task++){
if(task!=last){
int point =points [day][task]+ f(day-1, task , points,dp);
maxi=max(maxi,points);
}
}
return dp[day][last] = maxi;
}
int ninja(int n , vector<vector<int>>&points){
vector<vector<int>>dp(n, vector<int>(4,-1));//n+1 size
return f(n-1,3,points);
}
Grid Unique Paths : DP on Grids
We need to find the total unique paths from the top-left cell (matrix[0][0]) to the rightmost cell (matrix[M-1][N-1]).
At any cell we are allowed to move in only two directions:- bottom and right.
recurssion
int f(int i, int j){
if(i==0 && j==0)return 1; //reached its destination
if(i<0 && j<0 ) return 0; // out of boundary
int up= f(i-1, j);
int left= f(i,j-1);
return up+left;
}
int uniquepath(int m , int n){
return f(m-1,n-1);
}
memoization-
- Create a dp array of size [m][n] - kunki m-1 aur n-1 tk jana h to ek jyda declare krte h
int f(int i, int j, vector<vector<int>> &dp){
if(i==0 && j==0)return 1; //reached its destination
if(i<0 && j<0 ) return 0; // out of boundary
if(dp[i][j]!=-1) return dp[i][j];
int up= f(i-1, j, dp);
int left= f(i,j-1, dp);
return dp[i][j]= up+left;
}
int uniquepath(int m , int n){
vector<vector<int>> dp(m, vector<int>(n,-1)); // 2d vector
return f(m-1,n-1, dp);
}
Time Complexity: O(M*N)
Reason: At max, there will be M*N calls of recursion.
Space Complexity: O((N-1)+(M-1)) + O(M*N)
Reason: We are using a recursion stack space:O((N-1)+(M-1)), here (N-1)+(M-1) is the path length and an external DP Array of size ‘M*N’.
tabulation
int unique(int m, int n) {
int dp[m][n];
for(int i=0; i<m ;i++){
for(int j=0; j<n; j++){
//base condition
if(i==0 && j==0){
dp[i][j]=1;
continue;
}
int up=0;
int left = 0;
if(i>0)
up = dp[i-1][j];
if(j>0)
left = dp[i][j-1];
dp[i][j] = up+left;
}
}
return dp[m-1][n-1];
}
Time Complexity: O(M*N)
Reason: There are two nested loops
Space Complexity: O(M*N)
Reason: We are using an external array of size ‘M*N’’.
Minimum Path Sum In a Grid
We need to find a path from the top-left corner to the bottom-right corner of the matrix, such that there is a minimum cost past that we select.
At every cell, we can move in only two directions: right and bottom. The cost of a path is given as the sum of values of cells of the given matrix.
recurssion
inf f(int i, int j, vector<vector<int>> &grid)
{
if (i == 0 && j == 0)
return arr[0][0];
if (i < 0 && j < 0)
return INT_MAX; // out of boundary
int up = grid[i][j] + f(i - 1, j, grid);
int left = grid[i][j] + f(i, j - 1, grid);
return min(up, left);
}
int minsum(vector<vector<int>> &grid)
{
int n = grid.size();
int m = grid[0].size();
return f(n - 1, m - 1, grid);
}
memoization
inf f(int i, int j, vector<vector<int>>&grid, vector<vector<int>> &dp){
if(i==0 && j==0) return arr[0][0];
if(i<0 && j<0 ) return INT_MAX; // out of boundary
if(dp[i][j]!=-1) return dp[i][j];
int up = grid[i][j] + f( i-1, j , grid, dp);
int left = grid[i][j] + f(i, j-1, grid,dp);
return dp[i][j] = min(up, left);
}
int minsum(vector<vector<int>>&grid){
int n= grid.size();
vector<vector<int>> dp (n, (m,-1));
int m =grid[0].size();
return f(n-1, m-1, grid);
}
Time Complexity: O(N*M)
Reason: At max, there will be N*M calls of recursion.
Space Complexity: O((M-1)+(N-1)) + O(N*M)
Reason: We are using a recursion stack space:O((M-1)+(N-1)), here (M-1)+(N-1) is the path length and an external DP Array of size ‘N*M’.
tabulation
int minSumPath(int n, int m, vector<vector<int> > &grid){
int n = grid.size();
int m = grid[0].size();
vector<vector<int> > dp(n,vector<int>(m,0));
for(int i=0; i<n ; i++){
for(int j=0; j<m; j++){
if(i==0 && j==0) dp[i][j] = grid[i][j];
else{
int up = matrix[i][j];
if(i>0) up += dp[i-1][j];
else up += 1e9; // neglect
int left = matrix[i][j];
if(j>0) left+=dp[i][j-1];
else left += 1e9;
dp[i][j] = min(up,left);
}
}
}
return dp[n-1][m-1];
}
Time Complexity: O(N*M)
Reason: There are two nested loops
Space Complexity: O(N*M)
Reason: We are using an external array of size ‘N*M’’.
Minimum path sum in Triangular Grid- Fixed Starting and variable Ending Point
We need to find the minimum path sum from the first row to the last row.
At every cell we can move in only two directions: either to the bottom cell (↓) or to the bottom-right cell
recurssion
int f(int i, int j, vector<vector<int>>&triangle, int n){
if(i==n-1) return triangle[n-1][j]; // reach any col
d= triangle[i][j]+f(i+1, j, triangle , n);
dg = triangle[i][j]+f(i, j+1, triangle , n);
return min(d, dg);
}
int minimumpath(vector<vector<int>>&triangle){
return f(0, 0 , triangle , n);
}
memoization
int minimumPath(int i, int j, vector<vector<int> > &triangle,int n,
vector<vector<int> > &dp) {
if(dp[i][j]!=-1)
return dp[i][j];
if(i==n-1) return triangle[i][j];
int down = triangle[i][j]+minimumPath(i+1,j,triangle,n,dp);
int diagonal = triangle[i][j]+minimumPath(i+1,j+1,triangle,n,dp);
return dp[i][j] = min(down, diagonal);
}
int minimumPathSum(vector<vector<int> > &triangle, int n){
vector<vector<int> > dp(n,vector<int>(n,-1));
return minimumPath(0,0,triangle,n,dp);
}
Minimum path sum in Triangular Grid- variable Starting and fixed ending Point
recurssion- down to top
int f(int i, int j, int m, vector<vector<int>> &matrix){
// Base Conditions
if(j<0 || j>=m)
return -1e9; //out of boundary
if(i==0)
return matrix[0][j];
int up = matrix[i][j] + f(i-1,j,m,matrix);
int leftDiagonal = matrix[i][j] + f(i-1,j-1,m,matrix);
int rightDiagonal = matrix[i][j] + f(i-1,j+1,m,matrix);
return max(up,max(leftDiagonal,rightDiagonal));
}
int getMaxPathSum(vector<vector<int> > &matrix){
int n = matrix.size();
int m = matrix[0].size();
int maxi = INT_MIN;
//call all the column from the last row
for(int j=0; j<m;j++){
int ans = f(n-1,j,m,matrix);
maxi = max(maxi,ans);
}
return maxi;
}
memoization
int f(int i, int j, int m, vector<vector<int>> &matrix,
vector<vector<int> > &dp){
// Base Conditions
if(j<0 || j>=m)
return -1e9; //out of boundary
if(i==0)
return matrix[0][j];
if(dp[i][j]!=-1) return dp[i][j];
int up = matrix[i][j] + f(i-1,j,m,matrix,dp);
int leftDiagonal = matrix[i][j] + f(i-1,j-1,m,matrix,dp);
int rightDiagonal = matrix[i][j] + f(i-1,j+1,m,matrix,dp);
return dp[i][j]= max(up,max(leftDiagonal,rightDiagonal));
}
int getMaxPathSum(vector<vector<int> > &matrix){
int n = matrix.size();
int m = matrix[0].size();
vector<vector<int>> dp(n,vector<int>(m,-1)); //dp vector
int maxi = INT_MIN;
//call all the column from the last row
for(int j=0; j<m;j++){
int ans = f(n-1,j,m,matrix,dp);
maxi = max(maxi,ans);
}
return maxi;
}
Time Complexity: O(N*N)
Reason: At max, there will be M*N calls of recursion to solve a new problem,
Space Complexity: O(N) + O(N*M)
Reason: We are using a recursion stack space: O(N), where N is the path length and an external DP Array of size ‘N*M’.
DP ON SUBSEQUENCE - any contiguous (follow order)
**Subset Sum Equals to Target** →
generate all subsequence and check if any of them gives sum K
recurssion
int f(int ind, int target, vector<int>&arr){
if(target==0) return true;
if(ind==0) return (arr[0]==target);
bool nottake = f(ind-1, target, arr);
bool take= false;
if(arr[ind]<=target){
take= f(ind-1, target-arr[ind], arr);
}
return take | nottake ;
}
bool subsetsum(int n , int k, vector<int>&arr){
return f(n-1, k , arr);
}
TC- O(2^n) , sc - O(n)
memoization
TC- O(Nx target) , Sc - O(NX Target )+ O(n)
int f(int ind, int target, vector<int>&arr, vector<vector<int>>&dp){
if(target==0) return true;
if(ind==0) return (arr[0]==target);
if(dp[ind][target]!=-1) return dp[ind][target];
bool nottake = f(ind-1, target, arr, dp);
bool take= false;
if(arr[ind]<=target){
take= f(ind-1, target-arr[ind], arr, dp);
}
return dp[ind][targte] = take | nottake ;
}
bool subsetsum(int n , int k, vector<int>&arr){
vector<vector<int>>dp (n, vector<int>(k+1, -1));
return f(n-1, k , arr, dp);
}
Count subset with sum k
We are given an array ‘ARR’ with N positive integers and an integer K. We need to find the number of subsets whose sum is equal to K.
recurssion
inf f(int ind, int target, vector<int> &arr)
{
if (target == 0)
return 1;
if (ind == 0)
return (arr[0] == target);
int notpick = f(ind - 1, target, arr);
int pick = 0;
if (arr[ind] <= target)
{
pick = f(ind - 1, target - arr[i], arr);
}
return pick + notpick;
}
int findways(vector<int> &arr, int target)
{
int n = arr.size();
return f(n - 1, target, arr);
}
memoization
inf f( int ind , int target, vector<int>&arr, vector<vector<int>>&dp){
if(target==0) return 1;
if(ind==0) return (arr[0]==target);
if(dp[ind][taregt]!=-1) return dp[ind][target];
int notpick= f(ind-1, target, arr, dp);
int pick=0;
if(arr[ind]<=target){
pick= f(ind-1, target-arr[i], arr, dp);
}
return dp[ind][target]= pick + notpick;
}
int findways(vector<int>& arr, int target){
int n = arr.size();
vector<vector<int>>dp(n , vector<int>(target+1,-1));
return f(n-1, target, arr, dp);
}
0/1 knapsack
items has wt and val and a bag wt is given theif can steal only item till it does not exceed that bag weight. steal in such a way that value must be maximize
recurssion
TC - 2^n
sC - o(N)
int f(int ind, int bagwt, vector<int>&wt, vector<int>&val){
if(ind==0){
if(wt[0]<= bagwt) return val[0];// agr ek hi value h aur bag wt se choti h to pick to krega hi
else return 0;
}
int nottake= 0+ f(ind-1, bagwt, val);
int take=INT_MIN;// km hoga tbhi to pick krega
if(wt[ind]<=bagwt){
take= val[ind]+ f(ind-1, bagwt-wt[ind], val);
}
return max(nottake, take);
}
int knapsack(vector<int>weight , vector<int>value , int n , int maxweight){
return f(n-1,maxweight , weight , value );
}
tabulation
int knapsack(vector<int>wt , vector<int>val , int n , int maxweight){
vector<vector<int>>dp(n,vector<int>(maxweight+1, 0));
for(int W =wt[0]; W<=maxweight ; W++ ){
dp[0][W]= val[0]; //base case
}
for(int ind=1;ind<n;i++){
for(int W=0; W<=maxweight ; W++){
int nottake= dp[ind-1] [W];
int take=INT_MIN;// km hoga tbhi to pick krega
if(wt[ind]<=W){
take= val[ind] + dp[ind-1][W-wt[ind]];
}
return dp[ind][W]= max(nottake, take);
}
}
return dp[n-1][maxweight];
}
Time Complexity: O(N*W)
Reason: There are two nested loops
Space Complexity: O(N*W)
Reason: We are using an external array of size ‘N*W’. Stack Space is eliminated.
Minimum coins
min coins to form target
recurssion
int f(vector<int> &arr, int ind, int T)
{
if (ind == 0)
{
if (T % arr[0] == 0)
return T / arr[0];
else
return 1e9;
}
int notTaken = 0 + f(arr, ind - 1, T);
int taken = 1e9; // max
if (arr[ind] <= T)
taken = 1 + f(arr, ind, T - arr[ind]);
return min(notTaken, taken);
}
int minimumElements(vector<int> &arr, int T)
{
int n = arr.size();
int ans = f(arr, n - 1, T);
if (ans >= 1e9)
return -1;
return ans;
}
Unbounded Knapsack-. INFINITE SUPPLY
He can take a single item any number of times he wants and put it in his knapsack.
recurssion
int f(vector<int>& wt, vector<int>& val, int ind, int W,)
{
//W is the bag weight
if(ind == 0){
return ((int)(W/wt[0])) * val[0]; //no of time it can steel * val at index 0
}
int notTaken = 0 + f(wt,val,ind-1,W);
int taken = INT_MIN;
if(wt[ind] <= W)
taken = val[ind] + f(wt,val,ind,W-wt[ind]);
return max(notTaken,taken);
}
int unboundedKnapsack(int n, int W, vector<int>& val,vector<int>& wt) {
return f(wt, val, n-1, W);
}
TC - exponential
Sc - O(W)
tabulation
int unboundedKnapsack(int n, int W, vector<int>& val,vector<int>& wt) {
vector<vector<int>> dp(n,vector<int>(W+1,0));
//Base Condition
for(int i=wt[0]; i<=W; i++){
dp[0][i] = ((int) i/wt[0]) * val[0];
}
for(int ind =1; ind<n; ind++){
for(int cap=0; cap<=W; cap++){
int notTaken = 0 + dp[ind-1][cap];
int taken = INT_MIN;
if(wt[ind] <= cap)
taken = val[ind] + dp[ind][cap - wt[ind]];
dp[ind][cap] = max(notTaken, taken);
}
}
return dp[n-1][W];
}
Rod cutting problem
memoization
int f(vector<int>& price, int ind, int N, vector<vector<int>>& dp){
if(ind == 0){
return N*price[0];
}
if(dp[ind][N]!=-1)
return dp[ind][N];
int notTaken = 0 + f(price,ind-1,N,dp);
int taken = INT_MIN;
int rodLength = ind+1;
if(rodLength <= N)
taken = price[ind] + f(price,ind,N-rodLength,dp);
return dp[ind][N] = max(notTaken,taken);
}
int cutRod(vector<int>& price,int N) {
vector<vector<int>> dp(N,vector<int>(N+1,-1));
return f(price,N-1,N,dp);
}
Time Complexity: O(N*N)
Reason: There are N*(N+1) states therefore at max ‘N*(N+1)’ new problems will be solved.
Space Complexity: O(N*N) + O(N)
Reason: We are using a recursion stack space(O(N)) and a 2D array ( O(N*(N+1)).
DP ON STRINGS
Longest common subsequence - maintain order- THIS PRINT LENGTH
generate all subsequence and compare
int f(int i, int j , string &s1 , string &s2){
if(i<0 || j<0 ) return 0 ; //negative
if(s1[i]==s2[j])
return 1+ f(i-1, j-1, s1, s2); // match , both i and j will move one back
return max(f(i-1, j, s1, s2) /*not match ek hi pointer jayega peeche,*/ f(i-1,j-1,s1,s2));
}
int lcs(string s1, string s2){
int n = s1.size();
int m= s2.size();
return f(n-1, m-1, s1, s2);
}
memoization
int f(int i, int j, string &s1, string &s2, vector<vector<int>> &dp)
{
if (i < 0 || j < 0)
return 0; // negative
if (dp[i][j] != -1)
return dp[i][j];
if (s1[i] == s2[j])
return dp[i][j] = 1 + f(i - 1, j - 1, s1, s2, dp); // match , both i and j will move one back
return dp[i][j] = max(f(i - 1, j, s1, s2, dp) /*not match ek hi pointer jayega peeche,*/ f(i - 1, j - 1, s1, s2, dp));
}
int lcs(string s1, string s2)
{
int n = s1.size();
int m = s2.size();
vector<vector<int>> dp(n, vector<int>(m, -1));
return f(n - 1, m - 1, s1, s2, dp);
}
Tc - O(NXM)
SC- O(NXM) + O(N+M) - AUXILARY STACK SPACE
**Longest Repeating Subsequence**
Given string str, find the length of the longest repeating subsequence such that it can be found twice in the given string.
class Solution {
public:
int recur(int i,int j,string &str,int &n, vector<vector<int>> &dp){
if(i==n || j==n || i>j) return 0;
if(dp[i][j] !=-1) return dp[i][j];
int Not_take=0,Take=0;
Not_take = recur(i,j+1,str,n,dp);
if(str[i]!=str[j] || i==j)
Take = recur(i+1,j,str,n,dp);
else if(str[i] == str[j])
Take = 1+recur(i+1,j+1,str,n,dp);
return dp[i][j] = max(Not_take,Take);
}
int LongestRepeatingSubsequence(string str){
// Code here
int n = str.size();
vector<vector<int>> dp(n,vector<int>(n,-1));
return recur(0,0,str,n,dp);
}
};
DP ON STOCKS
Stock Buy and Sell |
We can buy and sell a stock only once.
We can buy and sell the stock on any day but to sell the stock, we need to first buy it on the same or any previous day.
keep track of minimal on the left
int maximumprofit(vector<int>&prices){
int mini= prices[0]; // phla element ko mini man lia
int maxpro=0;
int n= prices.size();
for(int i=1;i<n;i++){
int cost = prices[i]- mini; //buying - selling
int maxpro= max(maxpro, cost);
mini= min(mini, maxpro);
}
return maxpro;
}
TC - O(n)
Buy and Sell Stock – II
We can buy and sell the stock any number of times.
In order to sell the stock, we need to first buy it on the same or any previous day.
We can’t buy a stock again after buying it once. In other words, we first buy a stock and then sell it. After selling we can buy and sell again. But we can’t sell before buying and can’t buy before selling any previously bought stock.
recurssion
TC- 2^n
SC - O(n)
int f(int ind, int buy, int n, int *value){
if(ind==n) return 0;
int profit =0;
if(buy){
profit = max(-values[ind]/*- bcoz we are buying */ + f(ind+1, 0) /* buy so next day we cannot buy we will sell(take) */, 0 +f(ind+1,1)/* not intrested in buy and next day we can buy (not take)*/);
}
else{
profit = max(values[ind] +f(ind+1, 1),/* sell krdia to next day i can buy */ 0+ f(ind+1, 0)/* still holding a stock dont buy next*/);
}
return profit;
}
int getmax( int* value, int n){
return f(0,1,values, n);
}
memoization
TC- O(Nx2)
SC - O(nx2)+ auxilary stack space
int f(int ind, int buy, int n, int *value, vector<vector<int>>&dp){
if(ind==n) return 0;
if(dp[ind][buy]!=-1) return dp[ind][buy];
int profit =0;
if(buy){
profit = max(-values[ind]/*- bcoz we are buying */ + f(ind+1, 0, , n , values, dp) /* buy so next day we cannot buy we will sell(take) */, 0 +f(ind+1,1, n , values, dp)/* not intrested in buy and next day we can buy (not take)*/);
}
else{
profit = max(values[ind] +f(ind+1, 1, n , values, dp),/* sell krdia to next day i can buy */ 0+ f(ind+1, 0, n , values, dp)/* still holding a stock dont buy next*/);
}
return dp[ind][buy]= profit;
}
int getmax( int* value, int n){
vector<vector<int>>dp(n, vector<int>(2,-1));//2 bcoz ya to buy kr re ya sell
return f(0,1,values, n, dp);
}
Buy and Sell Stock – III
- We can buy and sell the stock any number of times.
- In order to sell the stock, we need to first buy it on the same or any previous day.
- We can’t buy a stock again after buying it once. In other words, we first buy a stock and then sell it. After selling we can buy and sell again. But we can’t sell before buying and can’t buy before selling any previously bought stock.
- We can do at most 2 transactions.
int f(vector<int>& Arr, int n, int ind, int buy, int cap, vector<vector<vector<int>>>& dp ){
if(ind==n || cap==0) return 0; //base case
if(dp[ind][buy][cap]!=-1)
return dp[ind][buy][cap];
int profit;
if(buy==0){// We can buy the stock
profit = max(0+f(Arr,n,ind+1,0,cap,dp)// not buy,
-Arr[ind] + f(Arr,n,ind+1,1,cap,dp)//buy);
}
if(buy==1){// We can sell the stock
profit = max(0+f(Arr,n,ind+1,1,cap,dp)//not sell,
Arr[ind] + f(Arr,n,ind+1,0,cap-1,dp)//sell);
}
return dp[ind][buy][cap] = profit;
}
int maxProfit(vector<int>& prices, int n)
{
// Creating a 3d - dp of size [n][2][3]
vector<vector<vector<int>>> dp(n,
vector<vector<int>>
(2,vector<int>(3,-1)));
return f(prices,n,0,0,2,dp);//cap=2
}
**Buy and Sell Stocks With Cooldown**
- We can’t buy a stock again after buying it once. In other words, we first buy a stock and then sell it. After selling we can buy and sell again. But we can’t sell before buying and can’t buy before selling any previously bought stock.
- We can’t buy a stock on the very next day of selling it. This is the cooldown clause.
int f(vector<int> Arr, int ind, int buy, int n, vector<vector<int>> &dp ){
if(ind>=n) return 0; //base case //outof boundary
if(dp[ind][buy]!=-1)
return dp[ind][buy];
int profit;
if(buy==0){// We can buy the stock
profit = max(0+f(Arr,ind+1,0,n,dp), -Arr[ind]+f(Arr,ind+1,1,n,dp));
}
if(buy==1){// We can sell the stock
profit = max(0+f(Arr,ind+1,1,n,dp), Arr[ind]+f(Arr,ind+2,0,n,dp)); //2md index pr jayega
}
return dp[ind][buy] = profit;
}
int stockProfit(vector<int> &Arr)
{
int n = Arr.size();
vector<vector<int>> dp(n,vector<int>(2,-1));
int ans = f(Arr,0,0,n,dp);
return ans;
}