Open In App

Tile Stacking Problem

Last Updated : 25 Nov, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Given integers n (the height of the tower), m (the maximum size of tiles available), and k (the maximum number of times each tile size can be used), the task is to calculate the number of distinct stable towers of height n that can be built.

Note:

  • A stable tower consists of exactly n tiles, each stacked such that no larger tile is placed on a smaller one.
  • Two towers are considered different if, at any height h (where 1<=h<=n), they have tiles of different sizes. 
Tile-Stacking-Problem

Examples: 

Input: n = 3, m = 3, k = 1.
Output: 1
Explanation: Possible sequence: [1, 2, 3].

Input: n = 3, m = 3, k = 2.
Output: 7
Explanation: Possible sequences: [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3], [1, 3, 3], [2, 2, 3], [2, 3, 3].

Using Recursion - O(k^m) Time and O(m) Space

The main idea for the recursive approach is to build the tower by selecting different numbers of tiles from the largest available tile size, moving to smaller sizes as we progress. For each tile size, we try adding up to k tiles (or fewer, if fewer are needed to reach height n). By reducing both the remaining height n and the largest available tile size m with each recursive call, we explore all valid combinations that maintain the stability condition (no larger tile placed on a smaller one).

The recurrence relation for this solution can be expressed as:

possibleWays(n, m, k) = sum of possibleWays(n - cnt, m - 1, k) for each cnt from 0 to min(k, n), where possibleWays(n, m, k) gives the number of ways to build a tower of height n using up to size m tiles, with a maximum of k tiles of any size allowed.

Base cases:

  • ways(0, m, k) = 1, meaning a tower of height 0 has only one possible configuration (using no tiles).
  • ways(n, 0, k) = 0, meaning no tower can be built if m is zero and n is greater than zero.
C++
// C++ program to find number of ways to
// make stable towers of given height.
#include <bits/stdc++.h>
using namespace std;

// Recursive function to calculate possible ways to
// form towers of height 'n' using tiles of sizes 
// up to 'm' with at most 'k' tiles of each size
int possibleWaysRecur(int n, int m, int k) {
    
    // Base cases: 
    // If height is 0, there is exactly 1 way (empty tower)
    if (n == 0) return 1;
    
    // If no more tile sizes are available, 
    // no ways can be formed
    if (m == 0) return 0;
    
    int ans = 0;
    
    // Try using 'cnt' tiles of the current size 
    // (from 0 up to 'k' tiles) and make recursive
    // calls for remaining height and tiles
    for (int cnt = 0; cnt <= k && cnt <= n; cnt++) {
        ans += possibleWaysRecur(n - cnt, m - 1, k);
    }
    
    return ans;
}

int possibleWays(int n, int m, int k) {
    return possibleWaysRecur(n, m, k);
}

int main() {
    int n = 3, m = 3, k = 2;
    cout << possibleWays(n, m, k) << endl;
    return 0;
}
Java
// Java program to find number of ways to
// make stable towers of given height.

class GfG {

    static int possibleWaysRecur(int n, int m, int k) {
        
        // Base cases: 
        // If height is 0, there is exactly 1 way (empty tower)
        if (n == 0) return 1;
        
        // If no more tile sizes are available, 
        // no ways can be formed
        if (m == 0) return 0;
        
        int ans = 0;
        
        // Try using 'cnt' tiles of the current size 
        // (from 0 up to 'k' tiles) and make recursive
        // calls for remaining height and tiles
        for (int cnt = 0; cnt <= k && cnt <= n; cnt++) {
            ans += possibleWaysRecur(n - cnt, m - 1, k);
        }
        
        return ans;
    }

    static int possibleWays(int n, int m, int k) {
        return possibleWaysRecur(n, m, k);
    }

    public static void main(String[] args) {
        int n = 3, m = 3, k = 2;
        System.out.println(possibleWays(n, m, k));
    }
}
Python
# Python program to find number of ways to
# make stable towers of given height.

def possibleWaysRecur(n, m, k):
    
    # Base cases: 
    # If height is 0, there is exactly 1 way (empty tower)
    if n == 0:
        return 1
    
    # If no more tile sizes are available, 
    # no ways can be formed
    if m == 0:
        return 0
    
    ans = 0
    
    # Try using 'cnt' tiles of the current size 
    # (from 0 up to 'k' tiles) and make recursive
    # calls for remaining height and tiles
    for cnt in range(min(k, n) + 1):
        ans += possibleWaysRecur(n - cnt, m - 1, k)
    
    return ans

def possibleWays(n, m, k):
    return possibleWaysRecur(n, m, k)

if __name__ == "__main__":
    n, m, k = 3, 3, 2
    print(possibleWays(n, m, k))
C#
// C# program to find number of ways to
// make stable towers of given height.

using System;

class GfG {

    static int possibleWaysRecur(int n, int m, int k) {
        
        // Base cases: 
        // If height is 0, there is exactly 1 way (empty tower)
        if (n == 0) return 1;
        
        // If no more tile sizes are available, 
        // no ways can be formed
        if (m == 0) return 0;
        
        int ans = 0;
        
        // Try using 'cnt' tiles of the current size 
        // (from 0 up to 'k' tiles) and make recursive
        // calls for remaining height and tiles
        for (int cnt = 0; cnt <= k && cnt <= n; cnt++) {
            ans += possibleWaysRecur(n - cnt, m - 1, k);
        }
        
        return ans;
    }

    static int possibleWays(int n, int m, int k) {
        return possibleWaysRecur(n, m, k);
    }

    static void Main() {
        int n = 3, m = 3, k = 2;
        Console.WriteLine(possibleWays(n, m, k));
    }
}
JavaScript
// JavaScript program to find number of ways to
// make stable towers of given height.

function possibleWaysRecur(n, m, k) {
    
    // Base cases: 
    // If height is 0, there is exactly 1 way (empty tower)
    if (n === 0) return 1;
    
    // If no more tile sizes are available, 
    // no ways can be formed
    if (m === 0) return 0;
    
    let ans = 0;
    
    // Try using 'cnt' tiles of the current size 
    // (from 0 up to 'k' tiles) and make recursive
    // calls for remaining height and tiles
    for (let cnt = 0; cnt <= k && cnt <= n; cnt++) {
        ans += possibleWaysRecur(n - cnt, m - 1, k);
    }
    
    return ans;
}

function possibleWays(n, m, k) {
    return possibleWaysRecur(n, m, k);
}

const n = 3, m = 3, k = 2;
console.log(possibleWays(n, m, k));

Output
7

Using Top-Down DP (Memoization) - O(n*m*k) Time and O(n*m) Space

If we notice carefully, we can observe that the above recursive solution holds the following two properties of Dynamic Programming.

1. Optimal Substructure: The solution to the tile stacking problem can be derived from the optimal solutions of smaller subproblems. Specifically, for any given n (height of tower) and m (sizes of tiles), we can express the recursive relation as follows:

  • possibleWays(n, m, k) = sum of possibleWays(n - cnt, m - 1, k) for each cnt from 0 to min(k, n).

2. Overlapping Subproblems: When implementing a recursive approach to solve the tile stacking problem, we observe that many subproblems are computed multiple times.

  • The recursive solution involves changing two parameters: n (height of tower) and the current tile size (m). We need to track both parameters, so we create a 2D array of size (n+1) x (m+1) because the value of n will be in the range [0, n] and m will be in the range [0, m+1].
  • We initialize the 2D array with -1 to indicate that no subproblems have been computed yet.
  • We check if the value at memo[n][m] is -1. If it is, we proceed to compute the result. otherwise, we return the stored result.
C++
// C++ program to find number of ways to
// make stable towers of given height.
#include <bits/stdc++.h>
using namespace std;

// Recursive function to calculate possible ways to
// form towers of height 'n' using tiles of sizes 
// up to 'm' with at most 'k' tiles of each size
int possibleWaysRecur(int n, int m, int k, 
                      vector<vector<int>> &memo) {
    
    // Base cases: 
    // If height is 0, there is exactly 1 way (empty tower)    
    if (n == 0) return 1;
        
    // If no more tile sizes are available, 
    // no ways can be formed
    if (m == 0) return 0;
    
    if (memo[n][m] != -1) return memo[n][m];
    
    int ans = 0;
    
    // Try using 'cnt' tiles of the current size 
    // (from 0 up to 'k' tiles) and make recursive
    // calls for remaining height and tiles
    for (int cnt = 0; cnt <= k && cnt <= n; cnt++) {
        ans += possibleWaysRecur(n - cnt, m - 1, k, memo);
    }
    
    return memo[n][m] = ans;
}

int possibleWays(int n, int m, int k) {
    vector<vector<int>> memo(n + 1, vector<int>(m + 1, -1));
    return possibleWaysRecur(n, m, k, memo);
}

int main() {
    int n = 3, m = 3, k = 2;
    cout << possibleWays(n, m, k) << endl;
    return 0;
}
Java
// Java program to find number of ways to
// make stable towers of given height.

class GfG {

    static int possibleWaysRecur(int n, int m, int k, int[][] memo) {
        
        // Base cases: 
        // If height is 0, there is exactly 1 way (empty tower)    
        if (n == 0) return 1;
        
        // If no more tile sizes are available, 
        // no ways can be formed
        if (m == 0) return 0;
        
        if (memo[n][m] != -1) return memo[n][m];
        
        int ans = 0;
        
        // Try using 'cnt' tiles of the current size 
        // (from 0 up to 'k' tiles) and make recursive
        // calls for remaining height and tiles
        for (int cnt = 0; cnt <= k && cnt <= n; cnt++) {
            ans += possibleWaysRecur(n - cnt, m - 1, k, memo);
        }
        
        return memo[n][m] = ans;
    }

    static int possibleWays(int n, int m, int k) {
        int[][] memo = new int[n + 1][m + 1];
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                memo[i][j] = -1;
            }
        }
        return possibleWaysRecur(n, m, k, memo);
    }

    public static void main(String[] args) {
        int n = 3, m = 3, k = 2;
        System.out.println(possibleWays(n, m, k));
    }
}
Python
# Python program to find number of ways to
# make stable towers of given height.

def possibleWaysRecur(n, m, k, memo):
    
    # Base cases: 
    # If height is 0, there is exactly 1 way (empty tower)    
    if n == 0:
        return 1
    
    # If no more tile sizes are available, 
    # no ways can be formed
    if m == 0:
        return 0
    
    if memo[n][m] != -1:
        return memo[n][m]
    
    ans = 0
    
    # Try using 'cnt' tiles of the current size 
    # (from 0 up to 'k' tiles) and make recursive
    # calls for remaining height and tiles
    for cnt in range(min(k, n) + 1):
        ans += possibleWaysRecur(n - cnt, m - 1, k, memo)
    
    memo[n][m] = ans
    return ans

def possibleWays(n, m, k):
    memo = [[-1 for _ in range(m + 1)] for _ in range(n + 1)]
    return possibleWaysRecur(n, m, k, memo)

if __name__ == "__main__":
    n, m, k = 3, 3, 2
    print(possibleWays(n, m, k))
C#
// C# program to find number of ways to
// make stable towers of given height.

using System;

class GfG {

    static int possibleWaysRecur(int n, int m, int k, int[,] memo) {
        
        // Base cases: 
        // If height is 0, there is exactly 1 way (empty tower)    
        if (n == 0) return 1;
        
        // If no more tile sizes are available, 
        // no ways can be formed
        if (m == 0) return 0;
        
        if (memo[n, m] != -1) return memo[n, m];
        
        int ans = 0;
        
        // Try using 'cnt' tiles of the current size 
        // (from 0 up to 'k' tiles) and make recursive
        // calls for remaining height and tiles
        for (int cnt = 0; cnt <= k && cnt <= n; cnt++) {
            ans += possibleWaysRecur(n - cnt, m - 1, k, memo);
        }
        
        memo[n, m] = ans;
        return ans;
    }

    static int possibleWays(int n, int m, int k) {
        int[,] memo = new int[n + 1, m + 1];
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                memo[i, j] = -1;
            }
        }
        return possibleWaysRecur(n, m, k, memo);
    }

    static void Main() {
        int n = 3, m = 3, k = 2;
        Console.WriteLine(possibleWays(n, m, k));
    }
}
JavaScript
// JavaScript program to find number of ways to
// make stable towers of given height.

function possibleWaysRecur(n, m, k, memo) {
    
    // Base cases: 
    // If height is 0, there is exactly 1 way (empty tower)    
    if (n === 0) return 1;
    
    // If no more tile sizes are available, 
    // no ways can be formed
    if (m === 0) return 0;
    
    if (memo[n][m] !== -1) return memo[n][m];
    
    let ans = 0;
    
    // Try using 'cnt' tiles of the current size 
    // (from 0 up to 'k' tiles) and make recursive
    // calls for remaining height and tiles
    for (let cnt = 0; cnt <= k && cnt <= n; cnt++) {
        ans += possibleWaysRecur(n - cnt, m - 1, k, memo);
    }
    
    memo[n][m] = ans;
    return ans;
}

function possibleWays(n, m, k) {
    let memo = Array.from({ length: n + 1 }, () => Array(m + 1).fill(-1));
    return possibleWaysRecur(n, m, k, memo);
}

const n = 3, m = 3, k = 2;
console.log(possibleWays(n, m, k));

Output
7

Using Bottom-Up DP (Tabulation) - O(n*m*k) Time and O(m*n) Space

The approach is similar to the previous one. Just instead of breaking down the problem recursively, we iteratively build up the solution by calculating in bottom-up manner.

The idea is to build the dp table where dp[i][j] represents the number of ways to form a tower of height i using tiles of sizes from 1 to j, with each tile being used at most k times.

The table is initialized with dp[0][j] = 1 for all j, as there is exactly one way to form a tower of height 0 (using no tiles). For each i (height) and j (tile size), the number of ways to form the tower is computed by first considering the ways without using the jth tile (dp[i][j] = dp[i][j-1]), and then adding the possibilities of using the jth tile from 1 to k times, provided the height i allows it (dp[i][j] += dp[i - cnt][j - 1] for each valid cnt).

C++
// C++ program to find number of ways to
// make stable towers of given height.
#include <bits/stdc++.h>
using namespace std;

// function to calculate possible ways to form
// towers of height 'n' using tiles of sizes up 
// to 'm' with at most 'k' tiles of each size
int possibleWays(int n, int m, int k) {
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
    
    // Base case for height 0
    for (int j = 0; j <= m; j++) dp[0][j] = 1;  
    
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            
            // Not using the jth tile at all
            dp[i][j] = dp[i][j - 1];  
            
            for (int cnt = 1; cnt <= k && cnt <= i; cnt++) {
                dp[i][j] += dp[i - cnt][j - 1];
            }
        }
    }
    
    return dp[n][m];
}

int main() {
    int n = 3, m = 3, k = 2;
    cout << possibleWays(n, m, k) << endl;
    return 0;
}
Java
// Java program to find number of ways to
// make stable towers of given height.

class GfG {

    static int possibleWays(int n, int m, int k) {
        int[][] dp = new int[n + 1][m + 1];
        
        // Base case for height 0
        for (int j = 0; j <= m; j++) dp[0][j] = 1;
        
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                
                // Not using the jth tile at all
                dp[i][j] = dp[i][j - 1];
                
                for (int cnt = 1; cnt <= k && cnt <= i; cnt++) {
                    dp[i][j] += dp[i - cnt][j - 1];
                }
            }
        }
        
        return dp[n][m];
    }

    public static void main(String[] args) {
        int n = 3, m = 3, k = 2;
        System.out.println(possibleWays(n, m, k));
    }
}
Python
# Python program to find number of ways to
# make stable towers of given height.

def possibleWays(n, m, k):
    dp = [[0 for _ in range(m + 1)] for _ in range(n + 1)]
    
    # Base case for height 0
    for j in range(m + 1):
        dp[0][j] = 1
    
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            
            # Not using the jth tile at all
            dp[i][j] = dp[i][j - 1]
            
            for cnt in range(1, min(k, i) + 1):
                dp[i][j] += dp[i - cnt][j - 1]
    
    return dp[n][m]

if __name__ == "__main__":
    n, m, k = 3, 3, 2
    print(possibleWays(n, m, k))
C#
// C# program to find number of ways to
// make stable towers of given height.

using System;

class GfG {

    static int possibleWays(int n, int m, int k) {
        int[,] dp = new int[n + 1, m + 1];
        
        // Base case for height 0
        for (int j = 0; j <= m; j++) dp[0, j] = 1;
        
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                
                // Not using the jth tile at all
                dp[i, j] = dp[i, j - 1];
                
                for (int cnt = 1; cnt <= k && cnt <= i; cnt++) {
                    dp[i, j] += dp[i - cnt, j - 1];
                }
            }
        }
        
        return dp[n, m];
    }

    static void Main() {
        int n = 3, m = 3, k = 2;
        Console.WriteLine(possibleWays(n, m, k));
    }
}
JavaScript
// JavaScript program to find number of ways to
// make stable towers of given height.

function possibleWays(n, m, k) {
    let dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0));
    
    // Base case for height 0
    for (let j = 0; j <= m; j++) dp[0][j] = 1;
    
    for (let i = 1; i <= n; i++) {
        for (let j = 1; j <= m; j++) {
            
            // Not using the jth tile at all
            dp[i][j] = dp[i][j - 1];
            
            for (let cnt = 1; cnt <= k && cnt <= i; cnt++) {
                dp[i][j] += dp[i - cnt][j - 1];
            }
        }
    }
    
    return dp[n][m];
}

const n = 3, m = 3, k = 2;
console.log(possibleWays(n, m, k));

Output
7

Using Space Optimized DP - O(m*n*k) Time and O(n) Space

From the previous approach, we can observe that that value of dp[i][j] depends only on the previous row, ie, i-1. There is no need to store all the previous states just one previous state is used to compute result.

C++
// C++ program to find number of ways to
// make stable towers of given height.
#include <bits/stdc++.h>
using namespace std;

int possibleWays(int n, int m, int k) {
    vector<int> curr(n + 1, 0), prev(n + 1, 0);
    
    // Base case for height 0
    prev[0] = 1;  
    
    for (int j = 1; j <= m; j++) {
        
        // Base case for height 0 in the current row
        curr[0] = 1;  
        
        for (int i = 1; i <= n; i++) {
            curr[i] = prev[i];
            for (int cnt = 1; cnt <= k && cnt <= i; cnt++) {
                curr[i] += prev[i - cnt];
            }
        }
        swap(curr, prev);
    }
    
    return prev[n];
}

int main() {
    int n = 3, m = 3, k = 2;
    cout << possibleWays(n, m, k) << endl;
    return 0;
}
Java
// Java program to find number of ways to
// make stable towers of given height.

class GfG {

    static int possibleWays(int n, int m, int k) {
        int[] curr = new int[n + 1];
        int[] prev = new int[n + 1];
        
        // Base case for height 0
        prev[0] = 1;
        
        for (int j = 1; j <= m; j++) {
            
            // Base case for height 0 in the current row
            curr[0] = 1;
            
            for (int i = 1; i <= n; i++) {
                curr[i] = prev[i];
                for (int cnt = 1; cnt <= k && cnt <= i; cnt++) {
                    curr[i] += prev[i - cnt];
                }
            }
          
            // Swap curr and prev arrays
            int[] temp = curr;
            curr = prev;
            prev = temp;
        }
        
        return prev[n];
    }

    public static void main(String[] args) {
        int n = 3, m = 3, k = 2;
        System.out.println(possibleWays(n, m, k));
    }
}
Python
# Python program to find number of ways to
# make stable towers of given height.

def possibleWays(n, m, k):
    curr = [0] * (n + 1)
    prev = [0] * (n + 1)
    
    # Base case for height 0
    prev[0] = 1
    
    for j in range(1, m + 1):
        
        # Base case for height 0 in the current row
        curr[0] = 1
        
        for i in range(1, n + 1):
            curr[i] = prev[i]
            for cnt in range(1, min(k, i) + 1):
                curr[i] += prev[i - cnt]
        
        # Swap curr and prev arrays
        prev, curr = curr, prev
    
    return prev[n]

if __name__ == "__main__":
    n, m, k = 3, 3, 2
    print(possibleWays(n, m, k))
C#
// C# program to find number of ways to
// make stable towers of given height.

using System;

class GfG {

    static int possibleWays(int n, int m, int k) {
        int[] curr = new int[n + 1];
        int[] prev = new int[n + 1];
        
        // Base case for height 0
        prev[0] = 1;
        
        for (int j = 1; j <= m; j++) {
            
            // Base case for height 0 in the current row
            curr[0] = 1;
            
            for (int i = 1; i <= n; i++) {
                curr[i] = prev[i];
                for (int cnt = 1; cnt <= k && cnt <= i; cnt++) {
                    curr[i] += prev[i - cnt];
                }
            }
          
            // Swap curr and prev arrays
            int[] temp = curr;
            curr = prev;
            prev = temp;
        }
        
        return prev[n];
    }

    static void Main() {
        int n = 3, m = 3, k = 2;
        Console.WriteLine(possibleWays(n, m, k));
    }
}
JavaScript
// JavaScript program to find number of ways to
// make stable towers of given height.

function possibleWays(n, m, k) {
    let curr = new Array(n + 1).fill(0);
    let prev = new Array(n + 1).fill(0);
    
    // Base case for height 0
    prev[0] = 1;
    
    for (let j = 1; j <= m; j++) {
        
        // Base case for height 0 in the current row
        curr[0] = 1;
        
        for (let i = 1; i <= n; i++) {
            curr[i] = prev[i];
            for (let cnt = 1; cnt <= k && cnt <= i; cnt++) {
                curr[i] += prev[i - cnt];
            }
        }
        // Swap curr and prev arrays
        [curr, prev] = [prev, curr];
    }
    
    return prev[n];
}

const n = 3, m = 3, k = 2;
console.log(possibleWays(n, m, k));

Output
7

Next Article

Similar Reads