Given a "2 x n" board and tiles of size "2 x 1", the task is to count the number of ways to tile the given board using the 2 x 1 tiles. A tile can either be placed horizontally i.e., as a 1 x 2 tile or vertically i.e., as 2 x 1 tile.
Examples:
Input: n = 4
Output: 5
Explanation: For a 2 x 4 board, there are 5 ways
- All 4 vertical (1 way)
- All 4 horizontal (1 way)
- 2 vertical and 2 horizontal (3 ways)
Input: n = 3
Output: 3
Explanation: We need 3 tiles to tile the board of size 2 x 3.
We can tile the board using the following ways
- Place all 3 tiles vertically.
- Place 1 tile vertically and the remaining 2 tiles horizontally (2 ways)
Using Recursion - O(2^n) Time and O(n) Space
The idea is to explore two possible ways to place tiles on a 2 x n board: either placing a vertical tile (2 x 1) which reduces the problem to filling a 2 x (n-1) board, or placing two horizontal tiles (1 x 2) which reduces the problem to filling a 2 x (n-2) board. At each step, we sum these two possibilities, creating a recursive solution where the total number of ways to tile the board is the sum of ways for the smaller subproblems.
Mathematically the recurrence relation will look like the following:
numberOfWays(n) = numberOfWays(n-1) + numberOfWays(n-2).
Base Cases:
- numberOfWays(n) = 1, if n = 0.
- numberOfWays(n) = 0, if n < 0.
C++
// C++ program to implement
// tiling problem using recursion
#include <bits/stdc++.h>
using namespace std;
int numberOfWays(int n) {
// Base Case: invalid n
if (n<0) return 0;
// Base case: valid n
if (n == 0) return 1;
int ans = 0;
// count ways if one tile
// is placed vertically
ans = numberOfWays(n-1);
// count ways if two tiles
// are placed horizontly.
ans += numberOfWays(n-2);
return ans;
}
int main() {
int n = 4;
cout<<numberOfWays(n);
return 0;
}
Java
// Java program to implement
// tiling problem using recursion
class GfG {
static int numberOfWays(int n) {
// Base Case: invalid n
if (n < 0) return 0;
// Base case: valid n
if (n == 0) return 1;
int ans = 0;
// count ways if one tile
// is placed vertically
ans = numberOfWays(n - 1);
// count ways if two tiles
// are placed horizontally.
ans += numberOfWays(n - 2);
return ans;
}
public static void main(String[] args) {
int n = 4;
System.out.println(numberOfWays(n));
}
}
Python
# Python program to implement
# tiling problem using recursion
def numberOfWays(n):
# Base Case: invalid n
if n < 0:
return 0
# Base case: valid n
if n == 0:
return 1
ans = 0
# count ways if one tile
# is placed vertically
ans = numberOfWays(n - 1)
# count ways if two tiles
# are placed horizontally.
ans += numberOfWays(n - 2)
return ans
if __name__ == "__main__":
n = 4
print(numberOfWays(n))
C#
// C# program to implement
// tiling problem using recursion
using System;
class GfG {
static int numberOfWays(int n) {
// Base Case: invalid n
if (n < 0) return 0;
// Base case: valid n
if (n == 0) return 1;
int ans = 0;
// count ways if one tile
// is placed vertically
ans = numberOfWays(n - 1);
// count ways if two tiles
// are placed horizontally.
ans += numberOfWays(n - 2);
return ans;
}
static void Main(string[] args) {
int n = 4;
Console.WriteLine(numberOfWays(n));
}
}
JavaScript
// JavaScript program to implement
// tiling problem using recursion
function numberOfWays(n) {
// Base Case: invalid n
if (n < 0) return 0;
// Base case: valid n
if (n === 0) return 1;
let ans = 0;
// count ways if one tile
// is placed vertically
ans = numberOfWays(n - 1);
// count ways if two tiles
// are placed horizontally.
ans += numberOfWays(n - 2);
return ans;
}
const n = 4;
console.log(numberOfWays(n));
Using Top-Down DP (Memoization) - O(n) Time and O(n) Space
If we notice carefully, we can observe that the above recursive solution holds the following two properties of Dynamic Programming:
1. Optimal Substructure: Number of ways to add i'th tile, i.e., numberOfWays(i), depends on the optimal solutions of the subproblems numberOfWays(i-1), and numberOfWays(i-2). By comparing these optimal substructures, we can efficiently calculate the number of ways to add i' th tile.
2. Overlapping Subproblems: While applying a recursive approach in this problem, we notice that certain subproblems are computed multiple times. For example, for n = 4, numberOfWays(3) and numberOfWays(2) are called. numberOfWays(3) again calls numberOfWays(2) which leads to Overlapping Subproblems.
- There are only is one parameter: i that changes in the recursive solution. So we create a 1D array of size n+1 for memoization.
- We initialize this array as -1 to indicate nothing is computed initially.
- Now we modify our recursive solution to first check if the value is -1, then only make recursive calls. This way, we avoid re-computations of the same subproblems.
C++
// C++ program to implement
// tiling problem using memoization
#include <bits/stdc++.h>
using namespace std;
int countRecur(int n, vector<int> &memo) {
// Base Case: invalid n
if (n<0) return 0;
// Base case: valid n
if (n == 0) return 1;
// If value is memoized
if (memo[n] != -1) return memo[n];
int ans = 0;
// count ways if one tile
// is placed vertically
ans = countRecur(n-1, memo);
// count ways if two tiles
// are placed horizontly.
ans += countRecur(n-2, memo);
return memo[n] = ans;
}
int numberOfWays(int n) {
vector<int> memo(n+1, -1);
return countRecur(n, memo);
}
int main() {
int n = 4;
cout<<numberOfWays(n);
return 0;
}
Java
// Java program to implement
// tiling problem using memoization
import java.util.Arrays;
class GfG {
static int countRecur(int n, int[] memo) {
// Base Case: invalid n
if (n < 0) return 0;
// Base case: valid n
if (n == 0) return 1;
// If value is memoized
if (memo[n] != -1) return memo[n];
int ans = 0;
// count ways if one tile
// is placed vertically
ans = countRecur(n - 1, memo);
// count ways if two tiles
// are placed horizontally.
ans += countRecur(n - 2, memo);
return memo[n] = ans;
}
static int numberOfWays(int n) {
int[] memo = new int[n + 1];
Arrays.fill(memo, -1);
return countRecur(n, memo);
}
public static void main(String[] args) {
int n = 4;
System.out.println(numberOfWays(n));
}
}
Python
# Python program to implement
# tiling problem using memoization
def countRecur(n, memo):
# Base Case: invalid n
if n < 0:
return 0
# Base case: valid n
if n == 0:
return 1
# If value is memoized
if memo[n] != -1:
return memo[n]
ans = 0
# count ways if one tile
# is placed vertically
ans = countRecur(n - 1, memo)
# count ways if two tiles
# are placed horizontally.
ans += countRecur(n - 2, memo)
memo[n] = ans
return memo[n]
def numberOfWays(n):
memo = [-1] * (n + 1)
return countRecur(n, memo)
if __name__ == "__main__":
n = 4
print(numberOfWays(n))
C#
// C# program to implement
// tiling problem using memoization
using System;
class GfG {
static int countRecur(int n, int[] memo) {
// Base Case: invalid n
if (n < 0) return 0;
// Base case: valid n
if (n == 0) return 1;
// If value is memoized
if (memo[n] != -1) return memo[n];
int ans = 0;
// count ways if one tile
// is placed vertically
ans = countRecur(n - 1, memo);
// count ways if two tiles
// are placed horizontally.
ans += countRecur(n - 2, memo);
return memo[n] = ans;
}
static int numberOfWays(int n) {
int[] memo = new int[n + 1];
for (int i = 0; i <= n; i++) {
memo[i] = -1;
}
return countRecur(n, memo);
}
static void Main(string[] args) {
int n = 4;
Console.WriteLine(numberOfWays(n));
}
}
JavaScript
// JavaScript program to implement
// tiling problem using memoization
function countRecur(n, memo) {
// Base Case: invalid n
if (n < 0) return 0;
// Base case: valid n
if (n === 0) return 1;
// If value is memoized
if (memo[n] !== -1) return memo[n];
let ans = 0;
// count ways if one tile
// is placed vertically
ans = countRecur(n - 1, memo);
// count ways if two tiles
// are placed horizontally.
ans += countRecur(n - 2, memo);
return memo[n] = ans;
}
function numberOfWays(n) {
const memo = Array(n + 1).fill(-1);
return countRecur(n, memo);
}
const n = 4;
console.log(numberOfWays(n));
Using Bottom-Up DP (Tabulation) - O(n) Time and O(n) Space
The idea is to fill the DP table based on previous values. For each tile, we either add it vertically or horizontally to compute number of ways. The table is filled in an iterative manner from i = 2 to n.
The dynamic programming relation is as follows:
- dp[i] = dp[i-1] + dp[i-2]
C++
// C++ program to implement
// tiling problem using tabulation
#include <bits/stdc++.h>
using namespace std;
int numberOfWays(int n) {
if (n==0 || n==1) return 1;
vector<int> dp(n+1);
dp[0] = 1;
dp[1] = 1;
for (int i=2; i<=n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
int main() {
int n = 4;
cout<<numberOfWays(n);
return 0;
}
Java
// Java program to implement
// tiling problem using tabulation
import java.util.*;
class GfG {
static int numberOfWays(int n) {
if (n == 0 || n == 1) return 1;
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
public static void main(String[] args) {
int n = 4;
System.out.println(numberOfWays(n));
}
}
Python
# Python program to implement
# tiling problem using tabulation
def numberOfWays(n):
if n == 0 or n == 1:
return 1
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
if __name__ == "__main__":
n = 4
print(numberOfWays(n))
C#
// C# program to implement
// tiling problem using tabulation
using System;
class GfG {
static int numberOfWays(int n) {
if (n == 0 || n == 1) return 1;
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
static void Main(string[] args) {
int n = 4;
Console.WriteLine(numberOfWays(n));
}
}
JavaScript
// JavaScript program to implement
// tiling problem using tabulation
function numberOfWays(n) {
if (n === 0 || n === 1) return 1;
const dp = new Array(n + 1).fill(0);
dp[0] = 1;
dp[1] = 1;
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
const n = 4;
console.log(numberOfWays(n));
Using Space Optimized DP - O(n) Time and O(1) Space
In previous approach of dynamic programming we have derived the relation between states as given below:
- dp[i] = dp[i-1] + dp[i-2]
If we observe that for calculating current dp[i] state we only need dp[i-1] and dp[i-2]. There is no need to store all the previous states just two previous state is used to compute result.
C++
// C++ program to implement
// tiling problem using space optimised dp
#include <bits/stdc++.h>
using namespace std;
int numberOfWays(int n) {
if (n==0 || n==1) return 1;
int prev2 = 1;
int prev1 = 1;
for (int i=2; i<=n; i++) {
int curr = prev1+prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
int main() {
int n = 4;
cout<<numberOfWays(n);
return 0;
}
Java
// Java program to implement
// tiling problem using space optimised dp
import java.util.*;
class GfG {
static int numberOfWays(int n) {
if (n == 0 || n == 1) return 1;
int prev2 = 1;
int prev1 = 1;
for (int i = 2; i <= n; i++) {
int curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
public static void main(String[] args) {
int n = 4;
System.out.println(numberOfWays(n));
}
}
Python
# Python program to implement
# tiling problem using space optimised dp
def numberOfWays(n):
if n == 0 or n == 1:
return 1
prev2 = 1
prev1 = 1
for i in range(2, n + 1):
curr = prev1 + prev2
prev2 = prev1
prev1 = curr
return prev1
if __name__ == "__main__":
n = 4
print(numberOfWays(n))
C#
// C# program to implement
// tiling problem using space optimised dp
using System;
class GfG {
static int numberOfWays(int n) {
if (n == 0 || n == 1) return 1;
int prev2 = 1;
int prev1 = 1;
for (int i = 2; i <= n; i++) {
int curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
static void Main(string[] args) {
int n = 4;
Console.WriteLine(numberOfWays(n));
}
}
JavaScript
// JavaScript program to implement
// tiling problem using space optimised dp
function numberOfWays(n) {
if (n === 0 || n === 1) return 1;
let prev2 = 1;
let prev1 = 1;
for (let i = 2; i <= n; i++) {
let curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
const n = 4;
console.log(numberOfWays(n));
Related articles:
Similar Reads
Josephus Problem Using Bit Magic The Problem This problem is named after Flavius Josephus a Jewish historian who fought against the Romans. According to Josephus he and his group of Jewish soldiers were cornered & surrounded by the Romans inside a cave, and they choose to murder and suicide inside of surrender and capture. They
14 min read
CSES Solutions - Another Game There are n heaps of coins and two players who move alternately. On each move, a player selects some of the nonempty heaps and removes one coin from each heap. The player who removes the last coin wins the game. Your task is to find out who wins if both players play optimally. Examples: Input: N = 3
5 min read
Problems not solved at the end of Nth day Given 3 integers K, P and N. Where, K is the number of problems given to the person every day and P is the maximum number of problems he can solve in a day. Find the total number of problems not solved after the N-th day.Examples: Input : K = 2, P = 1, N = 3 Output : 3 On each day 1 problem is left
3 min read
CSES Solutions - Reading Books There are N books, and Kotivalo and Justiina are going to read them all. For each book, you know the time it takes to read it, given as arr[] such that arr[i] is the time it takes to read ith book. They both read each book from beginning to end, and they cannot read a book at the same time. What is
7 min read
IBM Placement Paper | Logical Reasoning Set - 5 This is an IBM model placement paper on Logical Reasoning for placement preparation. This placement paper will cover logical reasoning questions that are asked in IBM recruitment drives and also strictly follows the pattern of questions asked in IBM interviews. It is recommended to solve each one of
8 min read