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
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++ Program to count the number of ways
// to express N as the sum of 1, 3, and 4
// using recursion
#include <iostream>
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 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 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# 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 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));
Output
6
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++ 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<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);
}
int countWays(int n) {
vector<int> memo(n+1, -1);
return countRecur(n, memo);
}
int main() {
int n = 5;
cout << countWays(n);
return 0;
}
// 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 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# 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 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));
Output
6
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++ 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<int> 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 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 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# 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 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));
Output
6
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++ 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 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 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# 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 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));
Output
6