Number of permutation with K inversions
Last Updated :
08 Mar, 2025
We are given two numbers n and k, the task is to find how many permutations of the first n number have exactly k inversion. Two elements in a sequence form an inversion if the smaller element appears before the larger element.
Examples:
Input: n = 3, k = 1
Output: 2
Explanation: Total Permutation of first 3 numbers are 123, 132, 213, 231, 312, 321
Permutation with 1 inversion: 132 and 213
Input: n = 3, k = 3
Output: 1
Explanation: Permutation with 3 inversions: 321
Using Recursion - O(n! * k) Time and O(n) Space
Base Cases:
- If n == 0, no permutations are possible, so return 0.
- If k == 0, there is exactly one permutation (the sorted order), so return 1.
Recursive Relation:
- For each possible position i where the nth element can be placed (where i ranges from 0 to min(k, n-1)), we recursively call the function to calculate the number of permutations of n-1 elements with k-i inversions. This represents placing the
n
th element in a way that creates i
inversions.
countPermWithkInversions(n, k) = Σ(countPermWithkInversions(n-1, k-i)) for i = 0 to min(k, n-1)
C++
// C++ code to count permutations with exactly
// k inversions using Recursion
#include <iostream>
using namespace std;
int countPermWithkInversions(int n, int k) {
// Base cases
// no permutations possible with 0 elements
if (n == 0) return 0;
// Only one way to have 0 inversions: sorted order
if (k == 0) return 1;
// Initialize result for this recursive step
int result = 0;
// Recursively sum up all valid counts
// by placing the nth largest element
// in positions that create the
// required inversion counts
for (int i = 0; i <= min(k, n - 1); i++) {
result += countPermWithkInversions(n - 1, k - i);
}
return result;
}
int main() {
int n = 4;
int k = 2;
cout << countPermWithkInversions(n, k);
return 0;
}
Java
// Java code to count permutations with
// exactly k inversions using Recursion
class GfG {
static int countPermWithkInversions(int n, int k) {
// Base cases
// no permutations possible with 0 elements
if (n == 0) return 0;
// Only one way to have 0 inversions: sorted order
if (k == 0) return 1;
// Initialize result for this recursive step
int result = 0;
// Recursively sum up all valid counts
// by placing the nth largest element
// in positions that create the
// required inversion counts
for (int i = 0; i <= Math.min(k, n - 1); i++) {
result += countPermWithkInversions(n - 1, k - i);
}
return result;
}
public static void main(String[] args) {
int n = 4;
int k = 2;
System.out.println(countPermWithkInversions(n, k));
}
}
Python
# Python code to count permutations with exactly
# k inversions using Recursion
def countPermWithkInversions(n, k):
# Base cases
# no permutations possible with 0 elements
if n == 0:
return 0
# Only one way to have 0 inversions: sorted order
if k == 0:
return 1
# Initialize result for this recursive step
result = 0
# Recursively sum up all valid counts
# by placing the nth largest element
# in positions that create the
# required inversion counts
for i in range(min(k, n - 1) + 1):
result += countPermWithkInversions(n - 1, k - i)
return result
if __name__ == "__main__":
n = 4
k = 2
print(countPermWithkInversions(n, k))
C#
// C# code to count permutations with exactly
// k inversions using Recursion
using System;
class GfG {
static int countPermWithkInversions(int n, int k) {
// Base cases
// no permutations possible with 0 elements
if (n == 0) return 0;
// Only one way to have 0 inversions: sorted order
if (k == 0) return 1;
// Initialize result for this recursive step
int result = 0;
// Recursively sum up all valid counts
// by placing the nth largest element
// in positions that create the
// required inversion counts
for (int i = 0; i <= Math.Min(k, n - 1); i++) {
result += countPermWithkInversions(n - 1, k - i);
}
return result;
}
static void Main() {
int n = 4;
int k = 2;
Console.WriteLine(countPermWithkInversions(n, k));
}
}
JavaScript
// Javascript code to count permutations with
// exactly k inversions using Recursion
function countPermWithkInversions(n, k) {
// Base cases
// no permutations possible with 0 elements
if (n === 0) return 0;
// Only one way to have 0 inversions: sorted order
if (k === 0) return 1;
// Initialize result for this recursive step
let result = 0;
// Recursively sum up all valid counts
// by placing the nth largest element
// in positions that create the
// required inversion counts
for (let i = 0; i <= Math.min(k, n - 1); i++) {
result += countPermWithkInversions(n - 1, k - i);
}
return result;
}
const n = 4;
const k = 2;
console.log(countPermWithkInversions(n, k));
Using Top-Down DP (Memoization) – O(n*k*k) Time and O(n*k) Space
The above recursive solution satisfies two key properties of Dynamic Programming, enabling us to use memoization for an optimized approach.
1. Optimal Substructure
We solve the problem of counting permutations with exactly k inversions by breaking it into subproblems involving fewer elements and inversions. Specifically, if we have n elements and k required inversions, we can compute it by trying positions for the largest element in a way that contributes to the inversion count.
The recursive relation is:
- countPermWithkInversions(n, k) = Σ countPermWithkInversions(n-1, k-i)
Here, we consider placing the largest element in positions that create i inversions, where i ranges from 0 to min(k, n-1).
2. Overlapping Subproblems:
Many subproblems are computed multiple times when finding permutations for smaller subsets and inversion counts. For example, while calculating the result for (n, k), subproblems for (n-1, k-i) are encountered repeatedly.
We use a 2D array memo of size (n+1) x (k+1), initialized to -1, to store results of subproblems. If a result is already stored in memo[n][k], we return it directly, avoiding redundant calculations.
C++
// C++ code to find number of permutation
// with k inversion using Memoization
#include <bits/stdc++.h>
using namespace std;
// Helper function for recursive computation
int kInversionsHelper(int n, int k,
vector<vector<int>>& memo) {
// Base case: If there are no elements,
// no permutations are possible
if (n == 0) return 0;
// Base case: If k is 0, only one way to
// have 0 inversions: sorted order
if (k == 0) return 1;
// Check if the result is already
// computed (memoization)
if (memo[n][k] != -1) return memo[n][k];
int result = 0;
// Loop through possible inversion counts
// for the nth largest element
for (int i = 0; i <= min(k, n - 1); i++) {
// Recursively count permutations for the
// remaining elements and inversions
result = (result +
kInversionsHelper(n - 1, k - i, memo));
}
return memo[n][k] = result;
}
// Function to count permutations with k inversions
int countPermWithkInversions(int n, int k) {
// Initialize memoization table with -1
// to indicate uncomputed values
vector<vector<int>> memo(n + 1, vector<int>(k + 1, -1));
return kInversionsHelper(n, k, memo);
}
int main() {
int n = 4;
int k = 2;
cout << countPermWithkInversions(n, k);
return 0;
}
Java
// Java code to find number of permutation
// with k inversions using Memoization
import java.util.Arrays;
class GfG {
// Helper function for recursive computation
static int kInversionsHelper(int n, int k, int[][] memo) {
// Base case: If there are no elements,
// no permutations are possible
if (n == 0) return 0;
// Base case: If k is 0, only one way to
// have 0 inversions: sorted order
if (k == 0) return 1;
// Check if the result is already
// computed (memoization)
if (memo[n][k] != -1) return memo[n][k];
int result = 0;
// Loop through possible inversion counts
// for the nth largest element
for (int i = 0; i <= Math.min(k, n - 1); i++) {
// Recursively count permutations for the
// remaining elements and inversions
result = (result
+ kInversionsHelper(n - 1, k - i, memo));
}
return memo[n][k] = result;
}
// Function to count permutations with k inversions
static int countPermWithkInversions(int n, int k) {
// Initialize memoization table with -1
// to indicate uncomputed values
int[][] memo = new int[n + 1][k + 1];
for (int[] row : memo) {
Arrays.fill(row, -1);
}
return kInversionsHelper(n, k, memo);
}
public static void main(String[] args) {
int n = 4;
int k = 2;
System.out.println(countPermWithkInversions(n, k));
}
}
Python
# Python code to find number of permutation
# with k inversions using Memoization
def kInversionsHelper(n, k, memo):
# Base case: If there are no elements,
# no permutations are possible
if n == 0:
return 0
# Base case: If k is 0, only one way to
# have 0 inversions: sorted order
if k == 0:
return 1
# Check if the result is already
# computed (memoization)
if memo[n][k] != -1:
return memo[n][k]
result = 0
# Loop through possible inversion counts
# for the nth largest element
for i in range(min(k, n - 1) + 1):
# Recursively count permutations for the
# remaining elements and inversions
result = (result + kInversionsHelper(n - 1, k - i, memo))
memo[n][k] = result
return result
def countPermWithkInversions(n, k):
# Initialize memoization table with -1
# to indicate uncomputed values
memo = [[-1 for _ in range(k + 1)] for _ in range(n + 1)]
return kInversionsHelper(n, k, memo)
if __name__ == "__main__":
n = 4
k = 2
print(countPermWithkInversions(n, k))
C#
// C# code to find number of permutation
// with k inversions using Memoization
using System;
class GfG {
// Helper function for recursive computation
static int kInversionsHelper(int n, int k, int[,] memo) {
// Base case: If there are no elements,
// no permutations are possible
if (n == 0) return 0;
// Base case: If k is 0, only one way to
// have 0 inversions: sorted order
if (k == 0) return 1;
// Check if the result is already
// computed (memoization)
if (memo[n, k] != -1) return memo[n, k];
int result = 0;
// Loop through possible inversion counts
// for the nth largest element
for (int i = 0; i <= Math.Min(k, n - 1); i++) {
// Recursively count permutations for the
// remaining elements and inversions
result = (result + kInversionsHelper(n - 1, k - i, memo));
}
return memo[n, k] = result;
}
static int countPermWithkInversions(int n, int k) {
// Initialize memoization table with -1
// to indicate uncomputed values
int[,] memo = new int[n + 1, k + 1];
for (int i = 0; i < memo.GetLength(0); i++) {
for (int j = 0; j < memo.GetLength(1); j++) {
memo[i, j] = -1;
}
}
return kInversionsHelper(n, k, memo);
}
static void Main() {
int n = 4;
int k = 2;
Console.WriteLine(countPermWithkInversions(n, k));
}
}
JavaScript
// JavaScript code to find number of permutation
// with k inversions using Memoization
function kInversionsHelper(n, k, memo) {
// Base case: If there are no elements,
// no permutations are possible
if (n === 0)
return 0;
// Base case: If k is 0, only one way to
// have 0 inversions: sorted order
if (k === 0)
return 1;
// Check if the result is already
// computed (memoization)
if (memo[n][k] !== -1)
return memo[n][k];
let result = 0;
// Loop through possible inversion counts
// for the nth largest element
for (let i = 0; i <= Math.min(k, n - 1); i++) {
// Recursively count permutations for the
// remaining elements and inversions
result = (result
+ kInversionsHelper(n - 1, k - i, memo));
}
memo[n][k] = result;
return result;
}
function countPermWithkInversions(n, k) {
// Initialize memoization table with -1
// to indicate uncomputed values
const memo = Array.from({length : n + 1},
() => Array(k + 1).fill(-1));
return kInversionsHelper(n, k, memo);
}
const n = 4;
const k = 2;
console.log(countPermWithkInversions(n, k));
Using Bottom-Up DP (Tabulation) – O(n*k*k) Time and O(n*k) Space
We create a 2D array dp of size (n + 1)*(k + 1), where the state dp[l][r] represents the number of permutations of l elements with exactly r inversions.
Base Case: dp[l][0] = 1 for all l, since there's only one way to have zero inversions-by arranging elements in sorted order.
For each element count l from 1 to n:
- For each inversion count r from 1 to k, calculate the number of valid permutations.
- To compute dp[l][r], we iterate over all possible positions of the largest element, which can create up to min(r, l-1) inversions, summing up the results from previous states
This relation fills the DP table by counting valid permutations based on subproblems:
- dp[l][r] = sum of dp[l-1][r-i] for all valid i.
The final result is stored in dp[n][k], representing the total permutations of n elements with exactly k inversions.
C++
// C++ code to find number of permutation
// with K inversions using Tabulation
#include <bits/stdc++.h>
using namespace std;
int countPermWithkInversions(int n, int k) {
// Initialize a 2D table for dynamic programming
vector<vector<int>> dp(n + 1, vector<int>(k + 1, 0));
// Base case: If k is 0, only one way to
// have 0 inversions: sorted order
for (int l = 0; l <= n; l++) {
dp[l][0] = 1;
}
// Fill the table using the tabulation method
for (int l = 1; l <= n; l++) {
for (int r = 1; r <= k; r++) {
for (int i = 0; i <= min(r, l - 1); i++) {
// Count permutations for the remaining
// elements and inversions
dp[l][r] = (dp[l][r] + dp[l - 1][r - i]);
}
}
}
return dp[n][k];
}
int main() {
int n = 4;
int k = 2;
cout << countPermWithkInversions(n, k);
return 0;
}
Java
// Java code to find number of permutation
// with K inversions using Tabulation
import java.util.Arrays;
class GfG {
static int countPermWithkInversions(int n, int k) {
// Initialize a 2D table for dynamic programming
int[][] dp = new int[n + 1][k + 1];
// Base case: If k is 0, only one way to
// have 0 inversions: sorted order
for (int l = 0; l <= n; l++) {
dp[l][0] = 1;
}
// Fill the table using the tabulation method
for (int l = 1; l <= n; l++) {
for (int r = 1; r <= k; r++) {
for (int i = 0; i <= Math.min(r, l - 1); i++) {
// Count permutations for the remaining
// elements and inversions
dp[l][r] = (dp[l][r] + dp[l - 1][r - i]);
}
}
}
return dp[n][k];
}
public static void main(String[] args) {
int n = 4;
int k = 2;
System.out.println(countPermWithkInversions(n, k));
}
}
Python
# Python code to find number of permutation
# with k inversions using Tabulation
def countPermWithkInversions(n, k):
# Initialize a 2D table for dynamic programming
dp = [[0] * (k + 1) for _ in range(n + 1)]
# Base case: If k is 0, only one way to
# have 0 inversions: sorted order
for l in range(n + 1):
dp[l][0] = 1
# Fill the table using the tabulation method
for l in range(1, n + 1):
for r in range(1, k + 1):
for i in range(min(r, l - 1) + 1):
# Count permutations for the remaining
# elements and inversions
dp[l][r] = (dp[l][r] + dp[l - 1][r - i])
return dp[n][k]
if __name__ == "__main__":
n = 4
k = 2
print(countPermWithkInversions(n, k))
C#
// C# code to find number of permutation
// with k inversions using Tabulation
using System;
class GfG {
static int countPermWithkInversions(int n, int k) {
// Initialize a 2D table for dynamic programming
int[,] dp = new int[n + 1, k + 1];
// Base case: If k is 0, only one way to
// have 0 inversions: sorted order
for (int l = 0; l <= n; l++) {
dp[l, 0] = 1;
}
// Fill the table using the tabulation method
for (int l = 1; l <= n; l++) {
for (int r = 1; r <= k; r++) {
for (int i = 0; i <= Math.Min(r, l - 1); i++) {
// Count permutations for the remaining
// elements and inversions
dp[l, r] = (dp[l, r] + dp[l - 1, r - i]);
}
}
}
return dp[n, k];
}
static void Main() {
int n = 4;
int k = 2;
Console.WriteLine(countPermWithkInversions(n, k));
}
}
JavaScript
// JavaScript code to find number of permutation
// with k inversions using Tabulation
function countPermWithkInversions(n, k) {
// Initialize a 2D table for dynamic programming
const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0));
// Base case: If k is 0, only one way to
// have 0 inversions: sorted order
for (let l = 0; l <= n; l++) {
dp[l][0] = 1;
}
// Fill the table using the tabulation method
for (let l = 1; l <= n; l++) {
for (let r = 1; r <= k; r++) {
for (let i = 0; i <= Math.min(r, l - 1); i++) {
// Count permutations for the remaining
// elements and inversions
dp[l][r] = (dp[l][r] + dp[l - 1][r - i]);
}
}
}
return dp[n][k];
}
let n = 4;
let k = 2;
console.log(countPermWithkInversions(n, k));
Time Complexity: O(n*k*k), where n is the number of elements and k is the number of inversions, due to the nested loops iterating through n, k, and possible inversions.
Auxiliary Space: O(n*k), because we maintain a 2D table of size (n+1)×(k+1) to store the computed values for dynamic programming.
Similar Reads
DSA Tutorial - Learn Data Structures and Algorithms DSA (Data Structures and Algorithms) is the study of organizing data efficiently using data structures like arrays, stacks, and trees, paired with step-by-step procedures (or algorithms) to solve problems effectively. Data structures manage how data is stored and accessed, while algorithms focus on
7 min read
Quick Sort QuickSort is a sorting algorithm based on the Divide and Conquer that picks an element as a pivot and partitions the given array around the picked pivot by placing the pivot in its correct position in the sorted array. It works on the principle of divide and conquer, breaking down the problem into s
12 min read
Merge Sort - Data Structure and Algorithms Tutorials Merge sort is a popular sorting algorithm known for its efficiency and stability. It follows the divide-and-conquer approach. It works by recursively dividing the input array into two halves, recursively sorting the two halves and finally merging them back together to obtain the sorted array. Merge
14 min read
Data Structures Tutorial Data structures are the fundamental building blocks of computer programming. They define how data is organized, stored, and manipulated within a program. Understanding data structures is very important for developing efficient and effective algorithms. What is Data Structure?A data structure is a st
2 min read
Bubble Sort Algorithm Bubble Sort is the simplest sorting algorithm that works by repeatedly swapping the adjacent elements if they are in the wrong order. This algorithm is not suitable for large data sets as its average and worst-case time complexity are quite high.We sort the array using multiple passes. After the fir
8 min read
Breadth First Search or BFS for a Graph Given a undirected graph represented by an adjacency list adj, where each adj[i] represents the list of vertices connected to vertex i. Perform a Breadth First Search (BFS) traversal starting from vertex 0, visiting vertices from left to right according to the adjacency list, and return a list conta
15+ min read
Binary Search Algorithm - Iterative and Recursive Implementation Binary Search Algorithm is a searching algorithm used in a sorted array by repeatedly dividing the search interval in half. The idea of binary search is to use the information that the array is sorted and reduce the time complexity to O(log N). Binary Search AlgorithmConditions to apply Binary Searc
15 min read
Insertion Sort Algorithm Insertion sort is a simple sorting algorithm that works by iteratively inserting each element of an unsorted list into its correct position in a sorted portion of the list. It is like sorting playing cards in your hands. You split the cards into two groups: the sorted cards and the unsorted cards. T
9 min read
Array Data Structure Guide In this article, we introduce array, implementation in different popular languages, its basic operations and commonly seen problems / interview questions. An array stores items (in case of C/C++ and Java Primitive Arrays) or their references (in case of Python, JS, Java Non-Primitive) at contiguous
4 min read
Sorting Algorithms A Sorting Algorithm is used to rearrange a given array or list of elements in an order. For example, a given array [10, 20, 5, 2] becomes [2, 5, 10, 20] after sorting in increasing order and becomes [20, 10, 5, 2] after sorting in decreasing order. There exist different sorting algorithms for differ
3 min read