Minimum number of given operations required to convert a permutation into an identity permutation

Given a permutation P (P1, P2, P3, … Pn) of first n natural numbers. Find the minimum number of operations to convert it into an identity permutation i.e. 1, 2, 3, …, n where each operation is defined as:
P[i] = P[P[P[i]]] \forall i from 1 to n (1 based indexing). If there is no way to convert then print -1.

Examples:

Input: arr[] = {2, 3, 1}
Output: 1
After 1 operation:
P[1] = P[P[P[1]]] = P[P[2]] = P[3] = 1
P[2] = P[P[P[2]]] = P[P[3]] = P[1] = 2
P[3] = P[P[P[3]]] = P[P[1]] = P[2] = 3
Thus after 1 operation we obtain an identity permutation.

Input: arr[] = {2, 1, 3}
Output: -1
There is no way to obtain identity permutation
no matter how many operations we apply.

Approach: First, find all the cycles in the given permutation. Here, a cycle is a directed graph in which there is an edge from an element e to the element on position e.
For example, Here’s the graph for permutation {4, 6, 5, 3, 2, 1, 8, 7}

Now in one operation, each cycle of length 3k breaks into 3 cycles each of length k while cycles of length 3k+1 or 3k+2 do not break. Since in the end, we need all cycles of length 1, therefore, all cycles must be a power of 3 otherwise answer doesn’t exist. The answer would then be the maximum of log(base 3) of all cycle lengths.

Below is the implementation of the above approach:

C++

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
  
int calculateCycleOperations(int len)
{
    int cycle_operations = 0;
    while (len) {
        len /= 3;
        ++cycle_operations;
    }
    return --cycle_operations;
}
  
// Function to return the minimum operations required
int minimumOperations(int p[], int n)
{
  
    // Array to keep track of visited elements
    bool visited[n + 1] = { 0 };
  
    // To store the final answer
    int ans = 0;
  
    // Looping through all the elements
    for (int i = 1; i <= n; i++) {
  
        // Current element
        int ele = p[i];
  
        // If current element is not present in the
        // previous cycles then only consider this
        if (!visited[ele]) {
  
            // Mark current element visited so that it
            // will not be considered in other cycles
            visited[ele] = 1;
  
            // To store the length of each cycle
            int len = 1;
            ele = p[ele];
  
            // Calculating cycle length
            while (!visited[ele]) {
                visited[ele] = 1;
                ++len;
                ele = p[ele];
            }
  
            // Operations needed for this cycle to reduce
            // to length 1 (if possible)
            int operations = calculateCycleOperations(len);
  
            // Checking cycle length to be power of 3
            // if not, then return -1
            int num = pow(3, operations);
            if (num != len) {
                return -1;
            }
  
            // Taking maximum of the operations
            ans = max(ans, operations);
        }
    }
    return ans;
}
  
// Driver code
int main()
{
    // 1-based indexing
    int P[] = { -1, 4, 6, 5, 3, 2, 7, 8, 9, 1 };
    int n = (sizeof(P) / sizeof(P[0])) - 1;
  
    // Calling function
    cout << minimumOperations(P, n);
  
    return 0;
}

chevron_right


Java

filter_none

edit
close

play_arrow

link
brightness_4
code

// Java implementation of the approach
import java.util.*;
  
class GFG
{
  
static int calculateCycleOperations(int len)
{
    int cycle_operations = 0;
    while (len > 0
    {
        len /= 3;
        ++cycle_operations;
    }
    return --cycle_operations;
}
  
// Function to return the minimum operations required
static int minimumOperations(int p[], int n)
{
  
    // Array to keep track of visited elements
    int []visited = new int[n+1];
    Arrays.fill(visited,0);
  
    // To store the final answer
    int ans = 0;
  
    // Looping through all the elements
    for (int i = 1; i <= n; i++) 
    {
  
        // Current element
        int ele = p[i];
  
        // If current element is not present in the
        // previous cycles then only consider this
        if (visited[ele] == 0
        {
  
            // Mark current element visited so that it
            // will not be considered in other cycles
            visited[ele] = 1;
  
            // To store the length of each cycle
            int len = 1;
            ele = p[ele];
  
            // Calculating cycle length
            while (visited[ele] == 0)
            {
                visited[ele] = 1;
                ++len;
                ele = p[ele];
            }
  
            // Operations needed for this cycle to reduce
            // to length 1 (if possible)
            int operations = calculateCycleOperations(len);
  
            // Checking cycle length to be power of 3
            // if not, then return -1
            int num = (int)Math.pow(3, operations);
            if (num != len) {
                return -1;
            }
  
            // Taking maximum of the operations
            ans = Math.max(ans, operations);
        }
    }
    return ans;
}
  
// Driver code
public static void main(String args[])
{
    // 1-based indexing
    int P[] = { -1, 4, 6, 5, 3, 2, 7, 8, 9, 1 };
    int n = P.length-1;
  
    // Calling function
    System.out.println(minimumOperations(P, n));
}
}
  
// This code is contributed by
// Surendra_Gangwar

chevron_right


Python3

filter_none

edit
close

play_arrow

link
brightness_4
code

# Python3 implementation of the approach
def calculateCycleOperations(length):
  
    cycle_operations = 0
    while length > 0
        length //= 3
        cycle_operations += 1
      
    return cycle_operations - 1
  
# Function to return the minimum 
# operations required
def minimumOperations(p, n):
  
    # Array to keep track of visited elements
    visited = [0] * (n + 1
  
    # To store the final answer
    ans = 0
  
    # Looping through all the elements
    for i in range(1, n + 1): 
  
        # Current element
        ele = p[i]
  
        # If current element is not present in the
        # previous cycles then only consider this
        if not visited[ele]: 
  
            # Mark current element visited so that it
            # will not be considered in other cycles
            visited[ele] = 1
  
            # To store the length of each cycle
            length = 1
            ele = p[ele]
  
            # Calculating cycle length
            while not visited[ele]: 
                visited[ele] = 1
                length += 1
                ele = p[ele]
              
            # Operations needed for this cycle to
            # reduce to length 1 (if possible)
            operations = calculateCycleOperations(length)
  
            # Checking cycle length to be power
            # of 3 if not, then return -1
            num = pow(3, operations)
            if num != length: 
                return -1
  
            # Taking maximum of the operations
            ans = max(ans, operations)
          
    return ans
  
# Driver code
if __name__ == "__main__":
  
    # 1-based indexing
    P = [-1, 4, 6, 5, 3, 2, 7, 8, 9, 1
    n = len(P) - 1
  
    # Calling function
    print(minimumOperations(P, n))
  
# This code is contributed by Rituraj Jain

chevron_right


C#

filter_none

edit
close

play_arrow

link
brightness_4
code

// C# implementation of the above approach 
using System;
  
class GFG 
  
    static int calculateCycleOperations(int len) 
    
        int cycle_operations = 0; 
        while (len > 0) 
        
            len /= 3; 
            ++cycle_operations; 
        
        return --cycle_operations; 
    
      
    // Function to return the minimum operations required 
    static int minimumOperations(int []p, int n) 
    
      
        // Array to keep track of visited elements 
        int []visited = new int[n+1]; 
  
        // To store the final answer 
        int ans = 0; 
      
        // Looping through all the elements 
        for (int i = 1; i <= n; i++) 
        
      
            // Current element 
            int ele = p[i]; 
      
            // If current element is not present in the 
            // previous cycles then only consider this 
            if (visited[ele] == 0) 
            
      
                // Mark current element visited so that it 
                // will not be considered in other cycles 
                visited[ele] = 1; 
      
                // To store the length of each cycle 
                int len = 1; 
                ele = p[ele]; 
      
                // Calculating cycle length 
                while (visited[ele] == 0) 
                
                    visited[ele] = 1; 
                    ++len; 
                    ele = p[ele]; 
                
      
                // Operations needed for this cycle to reduce 
                // to length 1 (if possible) 
                int operations = calculateCycleOperations(len); 
      
                // Checking cycle length to be power of 3 
                // if not, then return -1 
                int num = (int)Math.Pow(3, operations); 
                if (num != len) 
                
                    return -1; 
                
      
                // Taking maximum of the operations 
                ans = Math.Max(ans, operations); 
            
        
        return ans; 
    
      
    // Driver code 
    public static void Main() 
    
        // 1-based indexing 
        int []P = { -1, 4, 6, 5, 3, 2, 7, 8, 9, 1 }; 
        int n = P.Length-1; 
      
        // Calling function 
        Console.WriteLine(minimumOperations(P, n)); 
    
  
// This code is contributed by Ryuga

chevron_right


PHP

Output:

2

Time Complexity : O(N*LogN)



My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.