0% found this document useful (0 votes)
42 views13 pages

Dynammic Programming

This is a guide on Dynamic Programming. It is very exhaustive and covers a lot of cases relevant for Software Interviews.

Uploaded by

duppu7
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
42 views13 pages

Dynammic Programming

This is a guide on Dynamic Programming. It is very exhaustive and covers a lot of cases relevant for Software Interviews.

Uploaded by

duppu7
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

Dynamic Programming

1 1D DP
General Strategy
• Define the State:
Let dp[i] represent the best solution (according to the problem’s objective) consid-
ering only the first i elements.

• Identify the Recurrence:


Express dp[i] in terms of previous values, typically dp[j] for j < i, depending on the
decisions possible at index i:

dp[i] = combine(dp[j], cost or choice at i)

• Initialize Base Cases:


Carefully define dp[0], and possibly dp[1], dp[2], . . . depending on the recurrence.

• Build the DP Table:


1 for ( int i = 1; i <= n ; i ++) {
2 for ( auto valid_j : choices_for_i ) {
3 dp [ i ] = combine ( dp [ i ] , dp [ valid_j ] + cost ) ;
4 }
5 }

• Extract the Answer:


The final answer is often dp[n], or sometimes max(dp[i]) or min(dp[i]) over some
range.

Pseudocode Template
1 dp [0] = base case ;
2

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


4 dp [ i ] = default ; // 0 , INT_MAX , false , etc .
5

6 for ( all j such that transition from j to i is valid ) {


7 dp [ i ] = combine ( dp [ i ] , dp [ j ] + cost / choice ) ;
8 }

1
9 }
10

11 return dp [ n ] or max ( dp [ i ]) over some range ;

Example: House Robber Problem


Problem: Given an array arr, where arr[i] is the money in the ith house, find the
maximum money you can rob without robbing two adjacent houses.
• State: dp[i] = max money that can be robbed from first i houses
• Recurrence:
dp[i] = max(dp[i − 1], dp[i − 2] + arr[i])

• Base Cases:
dp[0] = arr[0], dp[1] = max(arr[0], arr[1])

• Answer: dp[n − 1]

2 2D DP
General Strategy
• Define the State:
Let dp[i][j] represent the optimal result (maximum, minimum, count, or boolean)
for a subproblem involving the first i elements in one dimension and j elements in
another.
• Understand the Problem’s Grid Structure:
2D DP is typically used when the problem involves:
– Two independent parameters (e.g., index and capacity in knapsack)
– A grid or matrix (e.g., path-finding, string comparison)
• Identify the Recurrence Relation:
Express dp[i][j] based on transitions from smaller subproblems:
dp[i][j] = combine of previous dp states like dp[i−1][j], dp[i][j−1], dp[i−1][j−1], etc.

• Initialize the Base Cases:


You often need to initialize entire rows or columns depending on what dp[i][j]
represents at the boundaries.
• Build the DP Table:
1 for ( int i = 0; i <= n ; i ++) {
2 for ( int j = 0; j <= m ; j ++) {
3 // Apply recurrence relation
4 dp [ i ][ j ] = combine of previous states ;
5 }
6 }

2
• Extract the Answer:
The final answer may be dp[n][m] or the max/min over the table, depending on the
problem.

Pseudocode Template
1 for ( int i = 0; i <= n ; i ++) {
2 for ( int j = 0; j <= m ; j ++) {
3 if ( base case condition )
4 dp [ i ][ j ] = base value ;
5 else
6 dp [ i ][ j ] = combine ( dp [i -1][ j ] , dp [ i ][ j -1] , dp [i
-1][ j -1] , ...) ;
7 }
8 }
9

10 return dp [ n ][ m ] or appropriate aggregate value ;

Example: Minimum Path Sum with Obstacles


Problem: Given an m × n grid grid, where each cell contains a non-negative integer
cost, and some cells may be blocked (denoted by -1), find the minimum cost to reach the
bottom-right corner from the top-left corner by only moving right or down. If no such
path exists, return -1.

• State: dp[i][j] = minimum cost to reach cell (i, j) from (0, 0)

• Recurrence:

−1
 ( if grid[i][j] = −1
dp[i][j] = dp[i − 1][j] if dp[i − 1][j] ̸= −1
grid[i][j] + min
 otherwise
dp[i][j − 1] if dp[i][j − 1] ̸= −1

• Base Case: (
grid[0][0] if grid[0][0] ̸= −1
dp[0][0] =
−1 otherwise

• Answer: dp[m − 1][n − 1]

Note: You must carefully handle blocked cells and propagate the ‘-1‘ state to prevent
invalid paths.

C++ Pseudocode

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


2 for ( int j = 0; j < n ; j ++) {
3 if ( grid [ i ][ j ] == -1) {
4 dp [ i ][ j ] = -1;

3
5 continue ;
6 }
7

8 if ( i == 0 && j == 0) {
9 dp [ i ][ j ] = grid [ i ][ j ];
10 } else {
11 int top = ( i > 0) ? dp [i -1][ j ] : -1;
12 int left = ( j > 0) ? dp [ i ][ j -1] : -1;
13

14 if ( top == -1 && left == -1)


15 dp [ i ][ j ] = -1;
16 else if ( top == -1)
17 dp [ i ][ j ] = left + grid [ i ][ j ];
18 else if ( left == -1)
19 dp [ i ][ j ] = top + grid [ i ][ j ];
20 else
21 dp [ i ][ j ] = min ( top , left ) + grid [ i ][ j ];
22 }
23 }
24 }

3 3D DP
General Strategy
• Define the State:
Let dp[i][j][k] represent the optimal result for three varying parameters. Often seen
in problems involving multiple agents, stages, or spatial layers.
• Recurrence:
Derive dp[i][j][k] from transitions that alter one or more of the dimensions, depend-
ing on constraints and interactions.
• Base Cases:
Carefully initialize all necessary (i, j, k) combinations, particularly when boundary
conditions block transitions.
• Build the Table:
1 for ( int i = 0; i <= A ; i ++) {
2 for ( int j = 0; j <= B ; j ++) {
3 for ( int k = 0; k <= C ; k ++) {
4 dp [ i ][ j ][ k ] = ... // Based on recurrence
5 }
6 }
7 }

• Answer:
Often dp[n][m][p] or the best result among a valid subspace.

4
Example: Ninja and Friends Problem
Problem: n days, 3 friends (0, 1, 2). Each day, each friend can perform an activity (0,
1, or 2) with an associated happiness score. Friends can’t do the same activity on the
same day. Maximize total happiness.

• State: dp[day][a][b] = max happiness if on ‘day‘, friend A did activity ‘a‘, and
friend B did activity ‘b‘.

• Transition: For each valid combination of previous day activities for friends A and
B:

dp[day][a][b] = max dp[day − 1][x][y] + hA [day][a] + hB [day][b] + hC [day][c]


x̸=a,y̸=b,x̸=y

where c is the third activity not equal to a or b.

• Base Case: dp[0][a][b] = hA [0][a] + hB [0][b] + hC [0][c] if a ̸= b ̸= c

• Answer: max dp[n − 1][a][b] over valid (a, b) combinations

C++ Pseudocode

1 for ( int a = 0; a < 3; a ++) {


2 for ( int b = 0; b < 3; b ++) {
3 if ( a == b ) continue ;
4 int c = 3 - a - b ;
5 dp [0][ a ][ b ] = happiness [0][ a ] + happiness [0][ b ] +
happiness [0][ c ];
6 }
7 }
8

9 for ( int day = 1; day < n ; day ++) {


10 for ( int a = 0; a < 3; a ++) {
11 for ( int b = 0; b < 3; b ++) {
12 if ( a == b ) continue ;
13 int c = 3 - a - b ;
14 for ( int x = 0; x < 3; x ++) {
15 for ( int y = 0; y < 3; y ++) {
16 if ( x == y || x == a || y == b || x == b
|| y == a ) continue ;
17 int z = 3 - x - y ;
18 dp [ day ][ a ][ b ] = max ( dp [ day ][ a ][ b ] , dp [ day
-1][ x ][ y ] +
19 happiness [ day ][ a ] +
happiness [ day ][ b ]
+ happiness [ day ][ c
]) ;
20 }
21 }
22 }

5
23 }
24 }
25

26 // Final answer = max over dp [n -1][ a ][ b ] with a != b

• Define the State:


Let dp[i][j][k] represent the optimal result for a subproblem with three parameters.

• Problem Types:
3D DP problems arise when we must track three varying dimensions. A common
scenario is when two agents are moving independently, and we must simulate both.

• Recurrence:
Transitions depend on how the three indices can change based on allowed move-
ments or actions:

dp[i][j][k] = combine(transitions from smaller states)

• Initialization:
Typically set the base case(s) when the indices are at their starting positions.

• Top-Down Memoization:
1 int dp [ N ][ N ][ N ];
2 memset ( dp , -1 , sizeof ( dp ) ) ;
3

4 int solve ( int r1 , int c1 , int c2 ) {


5 int r2 = r1 + c1 - c2 ; // since r1 + c1 == r2 + c2 at
every point
6

7 if ( r1 >= n || c1 >= n || r2 >= n || c2 >= n || grid [


r1 ][ c1 ] == -1 || grid [ r2 ][ c2 ] == -1)
8 return -1 e9 ;
9

10 if ( r1 == n - 1 && c1 == n - 1)
11 return grid [ r1 ][ c1 ];
12

13 if ( dp [ r1 ][ c1 ][ c2 ] != -1)
14 return dp [ r1 ][ c1 ][ c2 ];
15

16 int cherries = grid [ r1 ][ c1 ];


17 if ( c1 != c2 ) cherries += grid [ r2 ][ c2 ];
18

19 int best = max ({


20 solve ( r1 + 1 , c1 , c2 ) ,
21 solve ( r1 , c1 + 1 , c2 ) ,
22 solve ( r1 + 1 , c1 , c2 + 1) ,
23 solve ( r1 , c1 + 1 , c2 + 1)
24 }) ;

6
25

26 return dp [ r1 ][ c1 ][ c2 ] = cherries + best ;


27 }

• Extract the Answer: solve(0, 0, 0)

Example: Cherry Pickup


Problem: Given an n × n grid where each cell contains cherries (or −1 for thorns), two
players start at (0, 0) and move to (n − 1, n − 1) picking up cherries along the way. They
can only move down or right, and if they visit the same cell, they collect cherries only
once. Return the maximum cherries they can collect.
Constraints:

• Both players start at top-left and end at bottom-right.

• Both players take the same number of steps.

• A cell with −1 is impassable.

Approach: Let one player be at (r1, c1) and the other at (r2, c2), we reduce one
variable using r1 + c1 = r2 + c2, allowing 3D DP.
Final Answer: max(0, solve(0, 0, 0)) to handle cases where all paths are blocked.

4 DP on Subsequences
General Strategy
• Define the State:
Use multiple indices to represent the portion of the sequence considered. For ex-
ample, let dp[i][j] represent the best answer on the subarray or subsequence from
index i to j (inclusive), depending on the problem.

• Determine Transition:
Express dp[i][j] in terms of smaller subproblems. These are typically formed by
either including or excluding characters at ends, or choosing optimal partitions:

dp[i][j] = combine(dp[i + 1][j], dp[i][j − 1], maybe dp[i + 1][j − 1])

• Set Base Cases:


Often, the base cases are when the interval is of length 1 (e.g., dp[i][i]), or when
i > j (empty interval).

• Compute in Correct Order:


Ensure subproblems are computed before they are used. This usually means iter-
ating over increasing interval lengths or in reverse order.

• Extract the Answer:


Usually dp[0][n − 1], where n is the length of the array or string.

7
Pseudocode Template
1 for ( int i = 0; i < n ; i ++) {
2 dp [ i ][ i ] = base_case ; // usually 1 , 0 , or arr [ i ]
3 }
4

5 for ( int len = 2; len <= n ; len ++) {


6 for ( int i = 0; i + len - 1 < n ; i ++) {
7 int j = i + len - 1;
8 dp [ i ][ j ] = combine ( dp [ i +1][ j ] , dp [ i ][ j -1] , ...) ;
9 }
10 }
11

12 return dp [0][ n - 1];

Example: Longest Palindromic Subsequence


Problem: Given a string s, find the length of the longest subsequence that is a palin-
drome.

• State: dp[i][j] = length of longest palindromic subsequence in substring s[i . . . j]

• Recurrence:

1
 if i = j
dp[i][j] = 2 + dp[i + 1][j − 1] if s[i] = s[j]

max(dp[i + 1][j], dp[i][j − 1]) otherwise

• Base Cases: dp[i][i] = 1 for all i

• Answer: dp[0][n − 1]

Optimization Notes
• Space can often be reduced to O(n) if only recent rows are needed.

• For some problems, it’s possible to optimize transitions using segment trees, binary
search, or prefix sums.

• Consider the structure of the recurrence; if the problem satisfies the **monotonic-
ity of decision** or **quadrangle inequality**, use **Knuth** or **Divide and
Conquer** DP.

General Strategy
• Define the State:
Use indices and additional parameters (like current sum, count, or mask) to define
the subproblem. For this class of problems, dp[i][s] often represents whether a
subset sum s is achievable using the first i elements.

8
• Determine Transition:
Express dp[i][s] in terms of previous states, typically by either including or excluding
the ith element:
dp[i][s] = dp[i − 1][s] ∨ dp[i − 1][s − arr[i]]

• Set Base Cases:


dp[0][0] = true — zero sum is possible using no elements.
• Compute in Correct Order:
Iterate i from 1 to n and s from 0 to total sum. For space optimization, iterate s
in reverse.
• Extract the Answer:
Loop through possible subset sums s from 0 to ⌊total/2⌋, and find the one with
minimal absolute difference:
min |total − 2s| such that dp[n][s] = true

Pseudocode Template
1 int total = accumulate ( arr . begin () , arr . end () , 0) ;
2 vector < vector < bool > > dp ( n + 1 , vector < bool >( total + 1 , false )
);
3 dp [0][0] = true ;
4

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


6 for ( int s = 0; s <= total ; s ++) {
7 dp [ i ][ s ] = dp [ i - 1][ s ];
8 if ( s >= arr [ i - 1]) {
9 dp [ i ][ s ] = dp [ i ][ s ] || dp [ i - 1][ s - arr [ i - 1]];
10 }
11 }
12 }
13

14 int min_diff = INT_MAX ;


15 for ( int s = 0; s <= total / 2; s ++) {
16 if ( dp [ n ][ s ]) {
17 min_diff = min ( min_diff , abs ( total - 2 * s ) ) ;
18 }
19 }
20

21 return min_diff ;

Example: Partition into Two Subsets with Minimum Absolute


Difference
Problem: Given an array of positive integers, partition it into two subsets such that the
absolute difference between their sums is minimized.

9
• State: dp[i][s] = true if sum s is achievable with first i elements

• Transition:
dp[i][s] = dp[i − 1][s] ∨ dp[i − 1][s − arr[i − 1]]

• Base Case: dp[0][0] = true

• Answer:
min |total − 2s| such that dp[n][s] = true
0≤s≤⌊total/2⌋

Optimization Notes
• Space Optimization: Only the last row of the DP table is required. Use a 1D
array and iterate s in reverse.

• Bitset Trick: For faster implementation (and small values), use a bitset for subset
sums:
1 bitset < MAX_SUM > bs ;
2 bs [0] = 1;
3 for ( int x : arr ) bs |= ( bs << x ) ;

5 1D and 2D DP: Generalizability of the Knapsack


Problem
The knapsack problem is a versatile framework in dynamic programming (DP) that in-
volves selecting items to maximize value under a weight constraint. Its core idea—balancing
choices under constraints—appears in many Codeforces problems up to a 2000 rating.
Recognizing these problems as knapsack variants allows efficient application of DP tech-
niques.

General Strategy for Knapsack-Like Problems


The 0/1 knapsack problem involves choosing whether to include each item (with weight
wt[i] and value val[i]) in a knapsack of capacity W to maximize total value. This concept
generalizes to problems involving resource allocation, subset selection, or optimization
under constraints.

• Define the State:


Define dp[i][w] as the optimal solution (e.g., maximum value, minimum cost) con-
sidering the first i items and a resource constraint w (e.g., weight, time, or another
parameter). For 1D DP, use dp[w] if the problem depends only on the constraint.

• Identify the Recurrence:


Express dp[i][w] or dp[w] in terms of previous states, typically deciding whether to
include the i-th element:

dp[i][w] = max(dp[i − 1][w], dp[i − 1][w − wt[i − 1]] + val[i − 1]) if w ≥ wt[i − 1]

10
For 1D DP:

dp[w] = max(dp[w], dp[w − wt[i − 1]] + val[i − 1]) if w ≥ wt[i − 1]

• Initialize Base Cases:


Set dp[0][w] = 0 or dp[w] = 0 (no items or no capacity) or problem-specific defaults
(e.g., −∞ for maximization, ∞ for minimization).

• Build the DP Table:


1 for ( int i = 1; i <= n ; i ++) {
2 for ( int w = 0; w <= W ; w ++) {
3 dp [ i ][ w ] = dp [i -1][ w ]; // Exclude item
4 if ( w >= wt [i -1]) {
5 dp [ i ][ w ] = max ( dp [ i ][ w ] , dp [i -1][ w - wt [i -1]]
+ val [i -1]) ;
6 }
7 }
8 }

For 1D DP (space-optimized):
1 for ( int i = 1; i <= n ; i ++) {
2 for ( int w = W ; w >= wt [i -1]; w - -) {
3 dp [ w ] = max ( dp [ w ] , dp [ w - wt [i -1]] + val [i -1]) ;
4 }
5 }

• Extract the Answer:


The answer is typically dp[n][W ] or dp[W ], or the maximum/minimum over a range
of states.

Pseudocode Template
1 for ( int w = 0; w <= W ; w ++) {
2 dp [0][ w ] = 0; // Base case : no items
3 }
4 for ( int i = 0; i <= n ; i ++) {
5 dp [ i ][0] = 0; // Base case : no capacity
6 }
7

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


9 for ( int w = 0; w <= W ; w ++) {
10 dp [ i ][ w ] = dp [i -1][ w ]; // Exclude item
11 if ( w >= wt [i -1]) {
12 dp [ i ][ w ] = max ( dp [ i ][ w ] , dp [i -1][ w - wt [i -1]] +
val [i -1]) ;
13 }
14 }

11
15 }
16

17 return dp [ n ][ W ];

Applications in Codeforces Problems (Up to 2000 Rating)


The knapsack framework applies to many Codeforces problems by mapping items, weights,
and values to problem-specific entities. Below are examples of problems (up to 2000 rat-
ing) that can be solved as knapsack variants.

Example 1: Subset Sum Problem


Problem: Given an array of integers a1 , a2 , . . . , an and a target sum S, determine if
there exists a subset of the array that sums to S. (e.g., Codeforces 577B or similar)

• Knapsack Mapping: Treat each number ai as an item with weight ai and value
1 (if counting subsets) or 0 (if checking existence). The knapsack capacity is S.

• State: dp[i][s] = 1 if a subset of the first i elements sums to s, else 0. (Can optimize
to dp[s] for boolean checks.)

• Recurrence:

dp[i][s] = dp[i − 1][s] or dp[i − 1][s − a[i − 1]] if s ≥ a[i − 1]

• Base Cases:
dp[0][0] = 1, dp[0][s] = 0 for s > 0

• Answer: dp[n][S] = 1 indicates a valid subset exists.

Example 2: Minimum Cost to Achieve a Sum


Problem: Given n items with costs ci and values vi , find the minimum cost to achieve
a total value of at least V . (e.g., Codeforces 1203D2 or similar)

• Knapsack Mapping: Items have weights vi (contributing to value) and costs ci .


The knapsack capacity is the total value V . Minimize cost instead of maximizing
value.

• State: dp[i][v] = minimum cost using first i items to achieve value v

• Recurrence:

dp[i][v] = min(dp[i − 1][v], dp[i − 1][v − v[i − 1]] + c[i − 1]) if v ≥ v[i − 1]

• Base Cases:
dp[0][0] = 0, dp[0][v] = ∞ for v > 0

• Answer: min{dp[n][v] | v ≥ V }

12
Example 3: Longest Increasing Subsequence with Constraints
Problem: Given an array of numbers, find the longest increasing subsequence where the
sum of elements does not exceed a given limit S. (e.g., Codeforces 1354C2 or similar)

• Knapsack Mapping: Each element is an item with weight equal to its value ai
and value 1 (counting length). The knapsack capacity is S, with an additional
constraint on increasing order.

• State: dp[i][s] = maximum length of increasing subsequence using first i elements with sum ≤
s

• Recurrence:

dp[i][s] = max(dp[i−1][s], 1+max{dp[j][s−a[i−1]] | j < i, a[j] < a[i−1]}) if s ≥ a[i−1]

• Base Cases:
dp[0][s] = 0 ∀s

• Answer: dp[n][S]

Generalizability Tips for Codeforces


• Identify Constraints: Look for problems with a limited resource (e.g., sum, count,
time) and binary choices (include/exclude).

• Map to Knapsack: Translate problem elements to items, weights, and values.


For example, array elements might be items, their values might be weights, and
their contribution (e.g., count, score) might be values.

• Adapt the Objective: Knapsack can maximize value, minimize cost, or count
feasible solutions. Adjust the DP state and recurrence accordingly.

• Optimize for Rating 2000: Problems up to 2000 often require space or time
optimization (e.g., 1D DP instead of 2D) or handling additional constraints (e.g.,
order, modulo arithmetic).

13

You might also like