Domino and Tromino tiling problem
\\Given a positive integer N, the task is to find the number of ways to fill the board of dimension 2*N with a tile of dimensions 2 × 1, 1 × 2, (also known as domino) and an ‘L‘ shaped tile(also know as tromino) show below that can be rotated by 90 degrees.
The L shape tile:
XX
X
After rotating L shape tile by 90:
XX
X
or
X
XX
Examples:
Input: N = 3
Output: 5
Explanation:
Below is the image to illustrate all the combinations:
Input: N = 1
Output: 1
Approach: The given problem can be solved based on the following observations by:
Let’s define a 2 state, dynamic programming say dp[i, j] denoting one of the following arrangements in column index i.
- The current column can be filled with 1, 2 × 1 dominos in state 0, if the previous column had state 0.
- The current column can be filled with 2, 1 × 2 dominos horizontally in state 0, if the i – 2 column has state 0.
- The current column can be filled with an ‘L‘ shaped domino in state 1 and state 2, if the previous column had state 0.
- The current column can be filled with 1 × 2 shaped domino in state 1 if the previous column has state 2 or in state 2 if the previous column has state 1.
- Therefore, the transition of the state can be defined as the following:
- dp[i][0] = (dp[i – 1][0] + dp[i – 2][0]+ dp[i – 2][1] + dp[i – 2][2]).
- dp[i][1] = dp[i – 1][0] + dp[i – 1][2].
- dp[i][2] = dp[i – 1][0] + dp[i – 1][1].
Based on the above observations, follow the steps below to solve the problem:
- If the value of N is less than 3, then print N as the total number of ways.
- Initialize a 2-dimensional array, say dp[][3] that stores all the states of the dp.
- Consider the Base Case: dp[0][0] = dp[1][0] = dp[1][1] = dp[1][2] = 1.
- Iterate over the given range [2, N] and using the variable i and perform the following transitions in the dp as:
- dp[i][0] equals (dp[i – 1][0] + dp[i – 2][0]+ dp[i – 2][1] + dp[i – 2][2]).
- dp[i][1] equals dp[i – 1][0] + dp[i – 1][2].
- dp[i][2] equals dp[i – 1][0] + dp[i – 1][1].
- After completing the above steps, print the total number of ways stored in dp[N][0].
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1e9 + 7;
int numTilings( int N)
{
if (N < 3) {
return N;
}
vector<vector< long long > > dp(
N + 1, vector< long long >(3, 0));
dp[0][0] = dp[1][0] = 1;
dp[1][1] = dp[1][2] = 1;
for ( int i = 2; i <= N; i++) {
dp[i][0] = (dp[i - 1][0]
+ dp[i - 2][0]
+ dp[i - 2][1]
+ dp[i - 2][2])
% MOD;
dp[i][1] = (dp[i - 1][0]
+ dp[i - 1][2])
% MOD;
dp[i][2] = (dp[i - 1][0]
+ dp[i - 1][1])
% MOD;
}
return dp[N][0];
}
int main()
{
int N = 3;
cout << numTilings(N);
return 0;
}
|
Java
import java.util.Arrays;
class GFG{
public static long MOD = 1000000007l;
public static long numTilings( int N)
{
if (N < 3 )
{
return N;
}
long [][] dp = new long [N + 1 ][ 3 ];
for ( long [] row : dp)
{
Arrays.fill(row, 0 );
}
dp[ 0 ][ 0 ] = dp[ 1 ][ 0 ] = 1 ;
dp[ 1 ][ 1 ] = dp[ 1 ][ 2 ] = 1 ;
for ( int i = 2 ; i <= N; i++)
{
dp[i][ 0 ] = (dp[i - 1 ][ 0 ] + dp[i - 2 ][ 0 ] +
dp[i - 2 ][ 1 ] + dp[i - 2 ][ 2 ]) % MOD;
dp[i][ 1 ] = (dp[i - 1 ][ 0 ] + dp[i - 1 ][ 2 ]) % MOD;
dp[i][ 2 ] = (dp[i - 1 ][ 0 ] + dp[i - 1 ][ 1 ]) % MOD;
}
return dp[N][ 0 ];
}
public static void main(String args[])
{
int N = 3 ;
System.out.println(numTilings(N));
}
}
|
Python3
MOD = 1e9 + 7
def numTilings(N):
if (N < 3 ):
return N
dp = [[ 0 ] * 3 for i in range (N + 1 )]
dp[ 0 ][ 0 ] = dp[ 1 ][ 0 ] = 1
dp[ 1 ][ 1 ] = dp[ 1 ][ 2 ] = 1
for i in range ( 2 , N + 1 ):
dp[i][ 0 ] = (dp[i - 1 ][ 0 ] +
dp[i - 2 ][ 0 ] +
dp[i - 2 ][ 1 ] +
dp[i - 2 ][ 2 ]) % MOD
dp[i][ 1 ] = (dp[i - 1 ][ 0 ] +
dp[i - 1 ][ 2 ]) % MOD
dp[i][ 2 ] = (dp[i - 1 ][ 0 ] +
dp[i - 1 ][ 1 ]) % MOD
return int (dp[N][ 0 ])
N = 3
print (numTilings(N))
|
C#
using System;
using System.Collections.Generic;
class GFG{
static int MOD = 1000000007;
static int numTilings( int N)
{
if (N < 3) {
return N;
}
int [,]dp = new int [N+1,3];
dp[0,0] = dp[1,0] = 1;
dp[1,1] = dp[1,2] = 1;
for ( int i = 2; i <= N; i++) {
dp[i,0] = (dp[i - 1,0]
+ dp[i - 2,0]
+ dp[i - 2,1]
+ dp[i - 2,2])
% MOD;
dp[i,1] = (dp[i - 1,0]
+ dp[i - 1,2])
% MOD;
dp[i,2] = (dp[i - 1,0]
+ dp[i - 1,1])
% MOD;
}
return dp[N,0];
}
public static void Main()
{
int N = 3;
Console.Write(numTilings(N));
}
}
|
Javascript
<script>
const MOD = 1e9 + 7;
function numTilings(N) {
if (N < 3) {
return N;
}
let dp = Array(N + 1).fill().map(() => Array(3).fill(0))
dp[0][0] = dp[1][0] = 1;
dp[1][1] = dp[1][2] = 1;
for (let i = 2; i <= N; i++) {
dp[i][0] = (dp[i - 1][0]
+ dp[i - 2][0]
+ dp[i - 2][1]
+ dp[i - 2][2])
% MOD;
dp[i][1] = (dp[i - 1][0]
+ dp[i - 1][2])
% MOD;
dp[i][2] = (dp[i - 1][0]
+ dp[i - 1][1])
% MOD;
}
return dp[N][0];
}
let N = 3;
document.write(numTilings(N));
</script>
|
Time Complexity: O(N)
Auxiliary Space: O(N)
Easy Dp Algorithm Using Extra O(n) space.
C++
#include<bits/stdc++.h>
#include<iostream>
#define ll long long
ll mod=1e9+7;
using namespace std;
void the_solver( int n){
vector<ll>dp(n+1,0);
dp[0]=1;
dp[1]=1;dp[2]=2;dp[3]=5;
if (n<=3){
cout<<dp[n]<<endl;
return ;
}
for ( int i=4;i<=n;i++){
dp[i]=2*(dp[i-1])+dp[i-3];
dp[i]%=mod;
}
cout<<dp[n]<<endl;
return ;
}
signed main(){
int n=3;
the_solver(n);
return 0;
}
|
Java
import java.util.*;
class Main {
static final long mod = 1000000000L + 7 ;
static void theSolver( int n) {
long [] dp = new long [n + 1 ];
dp[ 0 ] = 1 ;
dp[ 1 ] = 1 ;
dp[ 2 ] = 2 ;
dp[ 3 ] = 5 ;
if (n <= 3 ) {
System.out.println(dp[n]);
return ;
}
for ( int i = 4 ; i <= n; i++) {
dp[i] = 2 * (dp[i - 1 ]) + dp[i - 3 ];
dp[i] %= mod;
}
System.out.println(dp[n]);
return ;
}
public static void main(String[] args) {
int n = 3 ;
theSolver(n);
}
}
|
Python3
mod = int ( 1e9 + 7 )
def the_solver(n):
dp = [ 0 ] * (n + 1 )
dp[ 0 ] = 1
dp[ 1 ] = 1
dp[ 2 ] = 2
dp[ 3 ] = 5
if n < = 3 :
print (dp[n])
return
for i in range ( 4 , n + 1 ):
dp[i] = 2 * dp[i - 1 ] + dp[i - 3 ]
dp[i] % = mod
print (dp[n])
return
n = 3
the_solver(n)
|
C#
using System;
using System.Linq;
using System.Collections.Generic;
class GFG
{
static int mod=1000000007;
static void the_solver( int n)
{
int [] dp= new int [n+1];
for ( int i=0; i<n+1; i++)
dp[i]=0;
dp[0]=1;
dp[1]=1;
dp[2]=2;
dp[3]=5;
if (n<=3){
Console.WriteLine(dp[n]);
return ;
}
for ( int i=4;i<=n;i++)
{
dp[i]=2*(dp[i-1])+dp[i-3];
dp[i]%=mod;
}
Console.WriteLine(dp[n]);
return ;
}
static public void Main()
{
int n=3;
the_solver(n);
}
}
|
Javascript
function the_solver(n)
{
let dp= new Array(n).fill(0);
dp[0]=1;
dp[1]=1;
dp[2]=2;
dp[3]=5;
if (n<=3)
{
document.write(dp[n]);
return ;
}
for (let i=4;i<=n;i++){
dp[i]=2*(dp[i-1])+dp[i-3];
dp[i]%=mod;
}
document.write(dp[n]);
return ;
}
let n=3;
the_solver(n);
|
Time Complexity: O(N).
Auxiliary Space: O(N).
Recursion Algorithm:
Types of domino and tromino
When no spaces
T1: Vertical domino
T2: Horizaontal domino
T3: Â 2 different types of Tromino
When there is a space
T4: Horizontal domino
T5/T6: 2 different types of Tromino depending upon the space
These steps describe a recursive algorithm to count the number of ways to tile a 2 x n grid using the given set of tiles, with T1 through T6 representing the different types of tiles.Â
No Previous Space Present:
- Place T1 and move to i+1: solve(i+1, previousGap=false)
- Place T2 in pair and move to i+2: solve(i+2, previousGap=false)
- Place either T3 or T4 (consider both cases) and move to i+2 with gap at i+1th column: 2*solve(i+2, previousGap=true)
Previous Space Present:
- Place T5 or T6 & fill previous gap (consider only 1 because depending on current configuration, only 1 grid out of them will fit) and move to i+1 with no previous gaps remaining: solve(i+1, previousGap=false)
- Place T2 & fill previous gap and move to i+1 with gap present in ith column: solve(i+1, previousGap=true)
C++
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1e9 + 7;
int func( int idx, int space, int n)
{
if (idx > n + 1) {
return 0;
}
if (idx == n + 1) {
if (space == true ) {
return 0;
}
return 1;
}
int cnt = 0;
if (space == false ) {
cnt += func(idx + 1, false , n);
cnt += func(idx + 2, false , n);
cnt += 2 * func(idx + 2, true , n);
}
else {
cnt += func(idx + 1, false , n);
cnt += func(idx + 1, true , n);
}
return cnt % MOD;
}
int numTilings( int N)
{ return func(1, false , N); }
int main()
{
int N = 3;
cout << numTilings(N);
return 0;
}
|
Java
import java.io.*;
import java.util.*;
public class Main {
static int MOD = 1000000007 ;
public static int func( int idx, boolean space, int n)
{
if (idx > n + 1 ) {
return 0 ;
}
if (idx == n + 1 ) {
if (space == true ) {
return 0 ;
}
return 1 ;
}
int cnt = 0 ;
if (space == false ) {
cnt += func(idx + 1 , false , n);
cnt += func(idx + 2 , false , n);
cnt += 2 * func(idx + 2 , true , n);
}
else {
cnt += func(idx + 1 , false , n);
cnt += func(idx + 1 , true , n);
}
return cnt % MOD;
}
public static int numTilings( int N){ return func( 1 , false , N); }
public static void main(String[] args) {
int N = 3 ;
System.out.println(numTilings(N));
}
}
|
Python3
MOD = int ( 1e9 + 7 )
def func(idx, space, n):
if idx > n + 1 :
return 0
if idx = = n + 1 :
if space = = True :
return 0
return 1
cnt = 0
if space = = False :
cnt + = func(idx + 1 , False , n)
cnt + = func(idx + 2 , False , n)
cnt + = 2 * func(idx + 2 , True , n)
else :
cnt + = func(idx + 1 , False , n)
cnt + = func(idx + 1 , True , n)
return cnt % MOD
def numTilings(N):
return func( 1 , False , N)
if __name__ = = "__main__" :
N = 3
print (numTilings(N))
|
C#
using System;
public class GFG
{
static int MOD = 1000000007;
public static int Func( int idx, bool space, int n)
{
if (idx > n + 1)
{
return 0;
}
if (idx == n + 1)
{
if (space == true )
{
return 0;
}
return 1;
}
int cnt = 0;
if (space == false )
{
cnt += Func(idx + 1, false , n);
cnt += Func(idx + 2, false , n);
cnt += 2 * Func(idx + 2, true , n);
}
else
{
cnt += Func(idx + 1, false , n);
cnt += Func(idx + 1, true , n);
}
return cnt % MOD;
}
public static int NumTilings( int N)
{
return Func(1, false , N);
}
public static void Main( string [] args)
{
int N = 3;
Console.WriteLine(NumTilings(N));
}
}
|
Javascript
const MOD = 1e9 + 7;
function func(idx, space, n) {
if (idx > n + 1) {
return 0;
}
if (idx === n + 1) {
if (space === true ) {
return 0;
}
return 1;
}
let cnt = 0;
if (space === false ) {
cnt += func(idx + 1, false , n);
cnt += func(idx + 2, false , n);
cnt += 2 * func(idx + 2, true , n);
} else {
cnt += func(idx + 1, false , n);
cnt += func(idx + 1, true , n);
}
return cnt % MOD;
}
function numTilings(N) {
return func(1, false , N);
}
const N = 3;
console.log(numTilings(N));
|
Time Complexity: O(3N) where N is the given number of columns of grid. We are branching out a max of 3 recursive calls each time and N such states giving total time complexity of O(3N)
Auxiliary Space: O(N), required for the recursive stack.
Memoization:
By analyzing the recursion tree from the previous approach, we can see that many redundant
 calls were made to the same function state. This is because the answer for a given set of parameters,Â
i and space, will always be the same. To avoid repeating these calculations, we can use dynamicÂ
programming and store the result in a dp array. The dp[i][space] entry represents the number of
 ways to tile a grid starting from the ith column and whether the previous column had a spaceor not.Â
If we find that dp[i][space] has already been calculated, we can simply return the stored result insteadÂ
of repeating the calculation.
We can cache the result of recursion to convert into a dp solution
C++
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1e9 + 7;
long long int func( long long int idx, bool space, int n,
vector<vector< int > >& dp)
{
if (idx > n + 1) {
return 0;
}
if (idx == n + 1) {
if (space == true ) {
return 0;
}
return 1;
}
if (dp[idx][space] != -1) {
return dp[idx][space];
}
long long int cnt = 0;
if (space == false ) {
cnt += func(idx + 1, false , n, dp);
cnt += func(idx + 2, false , n, dp);
cnt += 2 * func(idx + 2, true , n, dp);
}
else {
cnt += func(idx + 1, false , n, dp);
cnt += func(idx + 1, true , n, dp);
}
return dp[idx][space] = cnt % MOD;
}
int numTilings( int N)
{
vector<vector< int > > dp(N + 2, vector< int >(3, -1));
return func(1, false , N, dp);
}
int main()
{
int N = 3;
cout << numTilings(N);
return 0;
}
|
Java
import java.util.*;
public class GFG {
static final long MOD = ( long ) 1e9 + 7 ;
static long func( int idx, boolean space, int n, List<List<Long>> dp) {
if (idx > n + 1 ) {
return 0 ;
}
if (idx == n + 1 ) {
if (space == true ) {
return 0 ;
}
return 1 ;
}
if (dp.get(idx).get(space ? 1 : 0 ) != - 1 ) {
return dp.get(idx).get(space ? 1 : 0 );
}
long cnt = 0 ;
if (!space) {
cnt += func(idx + 1 , false , n, dp);
cnt += func(idx + 2 , false , n, dp);
cnt += 2 * func(idx + 2 , true , n, dp);
} else {
cnt += func(idx + 1 , false , n, dp);
cnt += func(idx + 1 , true , n, dp);
}
dp.get(idx).set(space ? 1 : 0 , cnt % MOD);
return dp.get(idx).get(space ? 1 : 0 );
}
static int numTilings( int N) {
List<List<Long>> dp = new ArrayList<>();
for ( int i = 0 ; i <= N + 1 ; i++) {
dp.add( new ArrayList<>(Arrays.asList(-1L, -1L)));
}
return ( int ) func( 1 , false , N, dp);
}
public static void main(String[] args) {
int N = 3 ;
System.out.println(numTilings(N));
}
}
|
Python3
def numTilings(N):
MOD = 10 * * 9 + 7
def func(idx, space, n, dp):
if idx > n + 1 :
return 0
if idx = = n + 1 :
if space = = True :
return 0
return 1
if dp[idx][ 1 if space else 0 ] ! = - 1 :
return dp[idx][ 1 if space else 0 ]
cnt = 0
if not space:
cnt + = func(idx + 1 , False , n, dp)
cnt + = func(idx + 2 , False , n, dp)
cnt + = 2 * func(idx + 2 , True , n, dp)
else :
cnt + = func(idx + 1 , False , n, dp)
cnt + = func(idx + 1 , True , n, dp)
dp[idx][ 1 if space else 0 ] = cnt % MOD
return dp[idx][ 1 if space else 0 ]
dp = [[ - 1 , - 1 ] for _ in range (N + 2 )]
return func( 1 , False , N, dp)
N = 3
print (numTilings(N))
|
C#
using System;
public class Program {
const long MOD = 1000000007;
public static long Func( int idx, bool space, int n,
long [][] dp)
{
if (idx > n + 1) {
return 0;
}
if (idx == n + 1) {
if (space == true ) {
return 0;
}
return 1;
}
if (dp[idx][space ? 1 : 0] != -1) {
return dp[idx][space ? 1 : 0];
}
long cnt = 0;
if (!space) {
cnt += Func(idx + 1, false , n, dp);
cnt += Func(idx + 2, false , n, dp);
cnt += 2 * Func(idx + 2, true , n, dp);
}
else {
cnt += Func(idx + 1, false , n, dp);
cnt += Func(idx + 1, true , n, dp);
}
return dp[idx][space ? 1 : 0] = cnt % MOD;
}
public static int NumTilings( int N)
{
long [][] dp = new long [N + 2][];
for ( int i = 0; i < dp.Length; i++) {
dp[i] = new long [3];
for ( int j = 0; j < dp[i].Length; j++) {
dp[i][j] = -1;
}
}
return ( int )Func(1, false , N, dp);
}
public static void Main()
{
int N = 3;
Console.WriteLine(NumTilings(N));
}
}
|
Javascript
function func(idx, space, n, dp, MOD) {
if (idx > n + 1) {
return 0;
}
if (idx === n + 1) {
if (space) {
return 0;
}
return 1;
}
if (dp[idx][space ? 1 : 0] !== -1) {
return dp[idx][space ? 1 : 0];
}
let cnt = 0;
if (!space) {
cnt += func(idx + 1, false , n, dp, MOD);
cnt += func(idx + 2, false , n, dp, MOD);
cnt += 2 * func(idx + 2, true , n, dp, MOD);
} else {
cnt += func(idx + 1, false , n, dp, MOD);
cnt += func(idx + 1, true , n, dp, MOD);
}
dp[idx][space ? 1 : 0] = cnt % MOD;
return dp[idx][space ? 1 : 0];
}
function numTilings(N) {
const MOD = 1e9 + 7;
const dp = Array.from({ length: N + 2 }, () => [-1, -1]);
return func(1, false , N, dp, MOD);
}
const N = 3;
console.log(numTilings(N));
|
Time Complexity : O(N) where N is the given number of columns of grid
Auxiliary Space : O(N), required for recursive stack and maintaining dp
Last Updated :
30 Nov, 2023
Like Article
Save Article
Share your thoughts in the comments
Please Login to comment...