Count of different ways to express N as the sum of 1, 3 and 4 (original) (raw)
Given a positive integer **n, the task is to **count the number of ways to express **n as a **sum of 1, 3 and 4.
**Examples:
**Input: n = 4
**Output: 4
**Explanation: There is 4 ways to represent 4 as sum of 1, 3 and 4: (1+1+1+1), (1+3), (3+1) and (4).**Input: n = 3
**Output: 2
**Explanation: There is 2 ways to represent 3 as sum of 1, 3 and 4: (1+1+1) and (3).
Table of Content
- Using Recursion - O(3^n) Time and O(n) Space
- Using Top-Down DP (Memoization) - O(n) Time and O(n) Space
- Using Bottom-Up DP (Tabulation) - O(n) Time and O(n) Space
- Using Space Optimized DP - O(n) Time and O(1) Space
Using Recursion - O(3^n) Time and O(n) Space
The idea is to recursively explore the possibilities of including **1, 3, or 4 in the sum. For any **n, the **result can be computed as the sum of ways for **n-1, n-3 and n-4.
Mathematically the recurrence relation will look like the following:
**countWays(n) = countWays(n-1) + countWays(n-3) + countWays(n-4).
**Base Cases:
- countWays(n) = 0, if n < 0.
- countWays(n) = 1, if n == 0.
C++ `
// C++ Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using recursion #include using namespace std;
int countWays(int n) {
// Base cases
if (n == 0) return 1;
if (n < 0) return 0;
// Recursive cases
return countWays(n - 1) +
countWays(n - 3) +
countWays(n - 4);}
int main() { int n = 5; cout << countWays(n); return 0; }
Java
// Java Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using recursion
class GfG {
static int countWays(int n) {
// Base cases
if (n == 0) return 1;
if (n < 0) return 0;
// Recursive cases
return countWays(n - 1) +
countWays(n - 3) +
countWays(n - 4);
}
public static void main(String[] args) {
int n = 5;
System.out.println(countWays(n));
}}
Python
Python Program to count the number of ways
to express N as the sum of 1, 3, and 4
using recursion
def countWays(n):
# Base cases
if n == 0:
return 1
if n < 0:
return 0
# Recursive cases
return countWays(n - 1) + countWays(n - 3) + countWays(n - 4)if name == "main": n = 5 print(countWays(n))
C#
// C# Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using recursion
using System;
class GfG {
static int countWays(int n) {
// Base cases
if (n == 0) return 1;
if (n < 0) return 0;
// Recursive cases
return countWays(n - 1) +
countWays(n - 3) +
countWays(n - 4);
}
static void Main(string[] args) {
int n = 5;
Console.WriteLine(countWays(n));
}}
JavaScript
// JavaScript Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using recursion
function countWays(n) {
// Base cases
if (n === 0) return 1;
if (n < 0) return 0;
// Recursive cases
return countWays(n - 1) +
countWays(n - 3) +
countWays(n - 4);}
let n = 5; console.log(countWays(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 make **sum n, i.e., **countWays(n), depends on the optimal solutions of the subproblems **countWays(n-1), countWays(n-3) and countWays(n-4). By combining these optimal substructures, we can efficiently calculate the number of ways to make sum **n.
**2. Overlapping Subproblems: While applying a recursive approach in this problem, we notice that certain subproblems are computed multiple times.
- There is only one parameter: **n that changes in the recursive solution. So we create a 1D array of size n 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 count the number of ways // to express N as the sum of 1, 3, and 4 // using memoization #include <bits/stdc++.h> using namespace std;
int countRecur(int n, vector &memo) {
// Base cases
if (n == 0) return 1;
if (n < 0) return 0;
// If value is memoized
if (memo[n]!=-1) return memo[n];
// Recursive cases
return memo[n] =
countRecur(n - 1, memo) +
countRecur(n - 3, memo) +
countRecur(n - 4, memo);}
int countWays(int n) { vector memo(n+1, -1); return countRecur(n, memo); }
int main() { int n = 5; cout << countWays(n); return 0; }
Java
// Java Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using memoization
import java.util.Arrays;
class GfG {
static int countRecur(int n, int[] memo) {
// Base cases
if (n == 0) return 1;
if (n < 0) return 0;
// If value is memoized
if (memo[n] != -1) return memo[n];
// Recursive cases
return memo[n] = countRecur(n - 1, memo) +
countRecur(n - 3, memo) +
countRecur(n - 4, memo);
}
static int countWays(int n) {
int[] memo = new int[n + 1];
Arrays.fill(memo, -1);
return countRecur(n, memo);
}
public static void main(String[] args) {
int n = 5;
System.out.println(countWays(n));
}}
Python
Python Program to count the number of ways
to express N as the sum of 1, 3, and 4
using memoization
def countRecur(n, memo):
# Base cases
if n == 0:
return 1
if n < 0:
return 0
# If value is memoized
if memo[n] != -1:
return memo[n]
# Recursive cases
memo[n] = countRecur(n - 1, memo) + \
countRecur(n - 3, memo) + countRecur(n - 4, memo)
return memo[n]def countWays(n): memo = [-1] * (n + 1) return countRecur(n, memo)
if name == "main": n = 5 print(countWays(n))
C#
// C# Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using memoization
using System;
class GfG {
static int countRecur(int n, int[] memo) {
// Base cases
if (n == 0) return 1;
if (n < 0) return 0;
// If value is memoized
if (memo[n] != -1) return memo[n];
// Recursive cases
return memo[n] = countRecur(n - 1, memo) +
countRecur(n - 3, memo) +
countRecur(n - 4, memo);
}
static int countWays(int n) {
int[] memo = new int[n + 1];
Array.Fill(memo, -1);
return countRecur(n, memo);
}
static void Main(string[] args) {
int n = 5;
Console.WriteLine(countWays(n));
}}
JavaScript
// JavaScript Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using memoization
function countRecur(n, memo) {
// Base cases
if (n === 0) return 1;
if (n < 0) return 0;
// If value is memoized
if (memo[n] !== -1) return memo[n];
// Recursive cases
return memo[n] = countRecur(n - 1, memo) +
countRecur(n - 3, memo) +
countRecur(n - 4, memo);}
function countWays(n) { const memo = Array(n + 1).fill(-1); return countRecur(n, memo); }
const n = 5; console.log(countWays(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 **n, its value is dependent on **n-1, n-3 and n-4. The table is filled in an iterative manner from i = 1 to i = n.
The dynamic programming relation is as follows:
- **dp[i] = sum(dp[i-j]) for value of j = {1, 3, 4} and i-j >=0.
C++ `
// C++ Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using tabulation #include <bits/stdc++.h> using namespace std;
int countWays(int n) { vector dp(n + 1, 0); dp[0] = 1;
for (int i = 1; i <= n; i++) {
// Calculate dp[i].
if (i - 1 >= 0)
dp[i] += dp[i - 1];
if (i - 3 >= 0)
dp[i] += dp[i - 3];
if (i - 4 >= 0)
dp[i] += dp[i - 4];
}
return dp[n];}
int main() {
int n = 5;
cout << countWays(n);
return 0;}
Java
// Java Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using tabulation
class GfG {
static int countWays(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
for (int i = 1; i <= n; i++) {
// Calculate dp[i].
if (i - 1 >= 0) dp[i] += dp[i - 1];
if (i - 3 >= 0) dp[i] += dp[i - 3];
if (i - 4 >= 0) dp[i] += dp[i - 4];
}
return dp[n];
}
public static void main(String[] args) {
int n = 5;
System.out.println(countWays(n));
}}
Python
Python Program to count the number of ways
to express N as the sum of 1, 3, and 4
using tabulation
def countWays(n): dp = [0] * (n + 1) dp[0] = 1
for i in range(1, n + 1):
# Calculate dp[i].
if i - 1 >= 0:
dp[i] += dp[i - 1]
if i - 3 >= 0:
dp[i] += dp[i - 3]
if i - 4 >= 0:
dp[i] += dp[i - 4]
return dp[n]if name == "main": n = 5 print(countWays(n))
C#
// C# Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using tabulation
using System;
class GfG {
static int countWays(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
for (int i = 1; i <= n; i++) {
// Calculate dp[i].
if (i - 1 >= 0) dp[i] += dp[i - 1];
if (i - 3 >= 0) dp[i] += dp[i - 3];
if (i - 4 >= 0) dp[i] += dp[i - 4];
}
return dp[n];
}
static void Main(string[] args) {
int n = 5;
Console.WriteLine(countWays(n));
}}
JavaScript
// JavaScript Program to count the number of ways // to express N as the sum of 1, 3, and 44 // using tabulation
function countWays(n) { const dp = Array(n + 1).fill(0); dp[0] = 1;
for (let i = 1; i <= n; i++) {
// Calculate dp[i].
if (i - 1 >= 0) dp[i] += dp[i - 1];
if (i - 3 >= 0) dp[i] += dp[i - 3];
if (i - 4 >= 0) dp[i] += dp[i - 4];
}
return dp[n];}
const n = 5; console.log(countWays(n));
`
Using Space Optimized DP - O(n) Time and O(1) Space
In **previous approach of dynamic programming we have derive the relation between states as given below:
- **dp[i] = sum(dp[i-j]) for value of j = {1, 3, 4} and i - j >=0.
If we observe that for calculating **current dp[i] state we only need previous **4 states of dp. There is no need to store all the **previous states.
C++ `
// C++ Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using space optimised dp #include <bits/stdc++.h> using namespace std;
int countWays(int n) {
// Return values for 1st
// 4 states
if (n==1) return 1;
if (n==2) return 1;
if (n==3) return 2;
if (n==4) return 4;
vector<int> dp(4);
dp[0] = 1;
dp[1] = 1;
dp[2] = 2;
dp[3] = 4;
for (int i=5; i<=n; i++) {
int curr = dp[0]+dp[1]+dp[3];
// Update values
dp[0] = dp[1];
dp[1] = dp[2];
dp[2] = dp[3];
dp[3] = curr;
}
return dp[3];}
int main() { int n = 5; cout << countWays(n); return 0; }
Java
// Java Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using space optimised dp
class GfG {
static int countWays(int n) {
// Return values for 1st
// 4 states
if (n == 1) return 1;
if (n == 2) return 1;
if (n == 3) return 2;
if (n == 4) return 4;
int[] dp = new int[4];
dp[0] = 1;
dp[1] = 1;
dp[2] = 2;
dp[3] = 4;
for (int i = 5; i <= n; i++) {
int curr = dp[0] + dp[1] + dp[3];
// Update values
dp[0] = dp[1];
dp[1] = dp[2];
dp[2] = dp[3];
dp[3] = curr;
}
return dp[3];
}
public static void main(String[] args) {
int n = 5;
System.out.println(countWays(n));
}}
Python
Python Program to count the number of ways
to express N as the sum of 1, 3, and 4
using space optimised dp
def countWays(n):
# Return values for 1st
# 4 states
if n == 1:
return 1
if n == 2:
return 1
if n == 3:
return 2
if n == 4:
return 4
dp = [1, 1, 2, 4]
for i in range(5, n + 1):
curr = dp[0] + dp[1] + dp[3]
# Update values
dp[0] = dp[1]
dp[1] = dp[2]
dp[2] = dp[3]
dp[3] = curr
return dp[3]if name == "main": n = 5 print(countWays(n))
C#
// C# Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using space optimised dp
using System;
class GfG {
static int countWays(int n) {
// Return values for 1st
// 4 states
if (n == 1) return 1;
if (n == 2) return 1;
if (n == 3) return 2;
if (n == 4) return 4;
int[] dp = new int[4];
dp[0] = 1;
dp[1] = 1;
dp[2] = 2;
dp[3] = 4;
for (int i = 5; i <= n; i++) {
int curr = dp[0] + dp[1] + dp[3];
// Update values
dp[0] = dp[1];
dp[1] = dp[2];
dp[2] = dp[3];
dp[3] = curr;
}
return dp[3];
}
static void Main(string[] args) {
int n = 5;
Console.WriteLine(countWays(n));
}}
JavaScript
// JavaScript Program to count the number of ways // to express N as the sum of 1, 3, and 4 // using space optimised dp
function countWays(n) {
// Return values for 1st
// 4 states
if (n === 1) return 1;
if (n === 2) return 1;
if (n === 3) return 2;
if (n === 4) return 4;
const dp = [1, 1, 2, 4];
for (let i = 5; i <= n; i++) {
const curr = dp[0] + dp[1] + dp[3];
// Update values
dp[0] = dp[1];
dp[1] = dp[2];
dp[2] = dp[3];
dp[3] = curr;
}
return dp[3];}
const n = 5; console.log(countWays(n));
`