Card Flip - GeeksforGeeks

Card Flip

Last Updated : 20 Oct, 2026

Given n cards arranged in a row. Each card has a number on the front and back, represented by two integer arrays front[] and back[]. For the i-th card, front[i] is the number on the front and back[i] is the number on the back. Initially, all cards are placed with their front side visible.

You can perform the following operation on the cards.

  • Choose any card at position i (1 ≤ i < n) and swap it with the card at position (i+1).
  • After swapping, flip both cards so that the opposite side becomes visible.

Determine the minimum number of operations required to arrange the cards such that the numbers on the visible sides are in non-decreasing order. If arranging the cards in non-decreasing order is impossible, return -1.

Examples:

Input: front[] = [4, 1, 3], back[] = [2, 1, 6]
Output: 1
Explanation: The current visible numbers are 4, 1, 3. If we choose i = 1, we would swap i = 1 and i = 2 and then flip the cards. Now the current visible numbers are 1, 2, 3 which are in non-decreasing order. Hence, in one operation the cards are placed in non-decreasing order.

Input: front[] = [2, 1], back[] = [4, 5]
Output: -1
Explanation: We can't make the visible side of cards non-decreasing in any number of operations.

Try It Yourself
redirect icon

[Naive Approach] Breadth-First Search (BFS) - O(n! * n) Time and O(n!) Space

The idea is to use BFS because each operation has the same cost of 1, so BFS explores states in increasing order of operations and guarantees the minimum number of operations.

  • Each state represents the order of the cards and their currently visible sides.
  • Starting from the initial arrangement, we generate new states by swapping every pair of adjacent cards and flipping both cards.
  • We maintain a visited set to avoid processing the same state again and return the number of operations when a state with non-decreasing visible numbers is found.
  • If no such state is reachable, we return -1.
C++
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <unordered_set>
#include <algorithm>
using namespace std;

int minOperations(vector<int> &front, vector<int> &back)
{
    int n = front.size();

    // Store the minimum operations for each state.
    // Each character represents a card and its visible side.
    string start;
    for (int i = 0; i < n; i++)
        start += char(2 * i);

    queue<pair<string, int>> q;
    unordered_set<string> visited;

    q.push({start, 0});
    visited.insert(start);

    while (!q.empty())
    {
        auto [state, steps] = q.front();
        q.pop();

        bool sorted = true;
        int prev = -1;

        // Check whether the visible numbers are sorted.
        for (char c : state)
        {
            int card = (unsigned char)c / 2;
            int side = (unsigned char)c % 2;
            int val = side ? back[card] : front[card];

            if (val < prev)
            {
                sorted = false;
                break;
            }
            prev = val;
        }

        if (sorted)
            return steps;

        // Try swapping every pair of adjacent cards.
        for (int i = 0; i < n - 1; i++)
        {
            string next = state;

            // Swap the cards and flip both visible sides.
            swap(next[i], next[i + 1]);
            next[i] ^= 1;
            next[i + 1] ^= 1;

            // Process only states not visited before.
            if (visited.insert(next).second)
                q.push({next, steps + 1});
        }
    }

    return -1;
}

int main()
{
    vector<int> front = {4, 1, 3};
    vector<int> back = {2, 1, 6};

    cout << minOperations(front, back) << endl;

    return 0;
}
Java
import java.util.*;

class GFG {
    static int minOperations(int[] front, int[] back)
    {
        int n = front.length;

        // Store the minimum operations for each state.
        // Each character represents a card and its visible
        // side.
        StringBuilder startBuilder = new StringBuilder();
        for (int i = 0; i < n; i++)
            startBuilder.append((char)(2 * i));

        String start = startBuilder.toString();

        Queue<State> q = new LinkedList<>();
        Set<String> visited = new HashSet<>();

        q.offer(new State(start, 0));
        visited.add(start);

        while (!q.isEmpty()) {
            State curr = q.poll();
            String state = curr.state;
            int steps = curr.steps;

            boolean sorted = true;
            int prev = -1;

            // Check whether the visible numbers are sorted.
            for (int i = 0; i < n; i++) {
                char c = state.charAt(i);
                int card = c / 2;
                int side = c % 2;
                int val
                    = side == 1 ? back[card] : front[card];

                if (val < prev) {
                    sorted = false;
                    break;
                }
                prev = val;
            }

            if (sorted)
                return steps;

            // Try swapping every pair of adjacent cards.
            for (int i = 0; i < n - 1; i++) {
                char[] next = state.toCharArray();

                // Swap the cards and flip both visible
                // sides.
                char temp = next[i];
                next[i] = next[i + 1];
                next[i + 1] = temp;

                next[i] ^= 1;
                next[i + 1] ^= 1;

                String nextState = new String(next);

                // Process only states not visited before.
                if (visited.add(nextState))
                    q.offer(
                        new State(nextState, steps + 1));
            }
        }

        return -1;
    }

    static class State {
        String state;
        int steps;

        State(String state, int steps)
        {
            this.state = state;
            this.steps = steps;
        }
    }

    public static void main(String[] args)
    {
        int[] front = { 4, 1, 3 };
        int[] back = { 2, 1, 6 };

        System.out.println(minOperations(front, back));
    }
}
Python
from collections import deque

def minOperations(front, back):
    n = len(front)

    # Store the minimum operations for each state.
    # Each character represents a card and its visible side.
    start = "".join(chr(2 * i) for i in range(n))

    q = deque([(start, 0)])
    visited = {start}

    while q:
        state, steps = q.popleft()

        sorted_order = True
        prev = -1

        # Check whether the visible numbers are sorted.
        for c in state:
            code = ord(c)
            card = code // 2
            side = code % 2
            val = back[card] if side else front[card]

            if val < prev:
                sorted_order = False
                break
            prev = val

        if sorted_order:
            return steps

        # Try swapping every pair of adjacent cards.
        for i in range(n - 1):
            next_state = list(state)

            # Swap the cards and flip both visible sides.
            next_state[i], next_state[i + 1] = (
                next_state[i + 1], next_state[i]
            )
            next_state[i] = chr(ord(next_state[i]) ^ 1)
            next_state[i + 1] = chr(ord(next_state[i + 1]) ^ 1)

            next_state = "".join(next_state)

            # Process only states not visited before.
            if next_state not in visited:
                visited.add(next_state)
                q.append((next_state, steps + 1))

    return -1


if __name__ == "__main__":
    front = [4, 1, 3]
    back = [2, 1, 6]

    print(minOperations(front, back))
C#
using System;
using System.Collections.Generic;
using System.Text;

class GFG {
    public int minOperations(int[] front, int[] back)
    {
        int n = front.Length;

        // Store the minimum operations for each state.
        // Each character represents a card and its visible
        // side.
        StringBuilder startBuilder = new StringBuilder();
        for (int i = 0; i < n; i++)
            startBuilder.Append((char)(2 * i));

        string start = startBuilder.ToString();

        Queue<NodeState> q = new Queue<NodeState>();
        HashSet<string> visited = new HashSet<string>();

        q.Enqueue(new NodeState(start, 0));
        visited.Add(start);

        while (q.Count > 0) {
            NodeState curr = q.Dequeue();
            string state = curr.StateValue;
            int steps = curr.Steps;

            bool sorted = true;
            int prev = -1;

            // Check whether the visible numbers are sorted.
            for (int i = 0; i < n; i++) {
                int code = state[i];
                int card = code / 2;
                int side = code % 2;
                int val
                    = side == 1 ? back[card] : front[card];

                if (val < prev) {
                    sorted = false;
                    break;
                }

                prev = val;
            }

            if (sorted)
                return steps;

            // Try swapping every pair of adjacent cards.
            for (int i = 0; i < n - 1; i++) {
                char[] next = state.ToCharArray();

                // Swap the cards and flip both visible
                // sides.
                char temp = next[i];
                next[i] = next[i + 1];
                next[i + 1] = temp;

                next[i] = (char)(next[i] ^ 1);
                next[i + 1] = (char)(next[i + 1] ^ 1);

                string nextState = new string(next);

                // Process only states not visited before.
                if (visited.Add(nextState))
                    q.Enqueue(new NodeState(nextState,
                                            steps + 1));
            }
        }

        return -1;
    }

    class NodeState {
        public string StateValue;
        public int Steps;

        public NodeState(string state, int steps)
        {
            StateValue = state;
            Steps = steps;
        }
    }

    static void Main()
    {
        int[] front = { 4, 1, 3 };
        int[] back = { 2, 1, 6 };

        GFG sol = new GFG();
        Console.WriteLine(sol.minOperations(front, back));
    }
}
JavaScript
function minOperations(front, back)
{
    let n = front.length;

    // Store the minimum operations for each state.
    // Each character represents a card and its visible
    // side.
    let start
        = Array
              .from({length : n},
                    (_, i) => String.fromCharCode(2 * i))
              .join("");

    let q = [ {state : start, steps : 0} ];
    let head = 0;
    let visited = new Set([ start ]);

    while (head < q.length) {
        let {state, steps} = q[head++];

        let sorted = true;
        let prev = -1;

        // Check whether the visible numbers are sorted.
        for (let c of state) {
            let code = c.charCodeAt(0);
            let card = Math.floor(code / 2);
            let side = code % 2;
            let val = side ? back[card] : front[card];

            if (val < prev) {
                sorted = false;
                break;
            }

            prev = val;
        }

        if (sorted)
            return steps;

        // Try swapping every pair of adjacent cards.
        for (let i = 0; i < n - 1; i++) {
            let next = state.split("");

            // Swap the cards and flip both visible sides.
            [next[i], next[i + 1]] =
                [ next[i + 1], next[i] ];

            next[i] = String.fromCharCode(
                next[i].charCodeAt(0) ^ 1);
            next[i + 1] = String.fromCharCode(
                next[i + 1].charCodeAt(0) ^ 1);

            let nextState = next.join("");

            // Process only states not visited before.
            if (!visited.has(nextState)) {
                visited.add(nextState);
                q.push(
                    {state : nextState, steps : steps + 1});
            }
        }
    }

    return -1;
}

// Driver Code
let front = [ 4, 1, 3 ];
let back = [ 2, 1, 6 ];

console.log(minOperations(front, back));

Output
1

[Expected Approach] Bitmask DP - O(30 * n * 2 ^ n) Time and O(30 * 2 ^ n) Space

The idea is to use bitmask DP, where each bit represents whether a card has been placed in the arrangement.

  • For each mask, we store the minimum operations required for every possible last visible number.
  • We try placing each unused card next, determine its visible side based on its position, and extend only those states where the visible numbers remain in non-decreasing order.
  • The cost of placing a card is the number of unplaced cards originally before it, and we return the minimum cost among all states containing every card.

Why Does This Approach Work?

  • Every valid arrangement can be constructed by selecting cards one by one, and the DP considers every possible choice.
  • Each transition correctly determines the visible side, checks the sorted order, and adds the required number of swaps.
  • By keeping the minimum cost for each DP state, the algorithm finds the minimum operations among all valid arrangements. If no valid arrangement exists, it returns -1.

Let's understand with an example: front[] = [4, 1, 3], back[] = [2, 1, 6]

Initialization: Set dp[0][0] = 0 and all other DP states to INF.

First card: Place card 0 first. Its front value 4 is visible, so dp[4][001] = 0.

Second card: Placing card 1 after card 0 is invalid because its visible value 1 is smaller than 4.
Instead, place card 1 first. Its back value 1 is visible, and the cost is 0.
Place card 0 next. Its back value 2 is visible, adding 1 swap. The arrangement becomes [1, 2].

Third card: Place card 2 next. Its front value 3 is visible, producing [1, 2, 3] with a total cost of 1.

Final Answer: The minimum cost for the complete mask 111 is 1. Hence, return 1.

C++
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int minOperations(vector<int> &front, vector<int> &back)
{
    int n = front.size();
    const int INF = 999999999;

    // dp[val][mask] stores the minimum operations needed
    // for the selected cards with last visible value val.
    vector<vector<int>> dp(31, vector<int>(1 << n, INF));

    // Initially, no cards are selected.
    dp[0][0] = 0;

    // Iterate through all subsets of cards.
    for (int mask = 0; mask < (1 << n); mask++)
    {
        int pos = __builtin_popcount(mask);

        // Try placing every unused card next.
        for (int k = 0; k < n; k++)
        {
            if (mask & (1 << k))
                continue;

            // Determine the visible side at the new position.
            int val = (pos + k) % 2 == 0 ? front[k] : back[k];

            int minVal = INF;

            // Find the minimum cost among valid previous values.
            for (int l = 0; l <= val; l++)
            {
                minVal = min(minVal, dp[l][mask]);
            }

            // Count the swaps needed to place this card.
            int extra = __builtin_popcount((~mask) & ((1 << k) - 1));

            int newMask = mask | (1 << k);

            // Update the DP state.
            if (minVal != INF)
            {
                dp[val][newMask] = min(dp[val][newMask], minVal + extra);
            }
        }
    }

    // Find the minimum cost for the complete arrangement.
    int minResult = INF;
    int fullMask = (1 << n) - 1;

    for (int val = 0; val <= 30; val++)
    {
        minResult = min(minResult, dp[val][fullMask]);
    }

    return minResult >= INF ? -1 : minResult;
}

int main()
{
    vector<int> front = {4, 1, 3};
    vector<int> back = {2, 1, 6};

    cout << minOperations(front, back) << endl;

    return 0;
}
Java
import java.util.*;

class GFG {
    public int minOperations(int[] front, int[] back)
    {
        int n = front.length;
        int INF = 999999999;

        // dp[val][mask] stores the minimum operations
        // needed for the selected cards with last visible
        // value val.
        int[][] dp = new int[31][1 << n];

        for (int i = 0; i <= 30; i++)
            Arrays.fill(dp[i], INF);

        // Initially, no cards are selected.
        dp[0][0] = 0;

        // Iterate through all subsets of cards.
        for (int mask = 0; mask < (1 << n); mask++) {
            int pos = Integer.bitCount(mask);

            // Try placing every unused card next.
            for (int k = 0; k < n; k++) {
                if ((mask & (1 << k)) != 0)
                    continue;

                // Determine the visible side at the new
                // position.
                int val = (pos + k) % 2 == 0 ? front[k]
                                             : back[k];

                int minVal = INF;

                // Find the minimum cost among valid
                // previous values.
                for (int l = 0; l <= val; l++)
                    minVal = Math.min(minVal, dp[l][mask]);

                // Count the swaps needed to place this
                // card.
                int extra = Integer.bitCount(
                    (~mask) & ((1 << k) - 1));

                int newMask = mask | (1 << k);

                // Update the DP state.
                if (minVal != INF) {
                    dp[val][newMask] = Math.min(
                        dp[val][newMask], minVal + extra);
                }
            }
        }

        // Find the minimum cost for the complete
        // arrangement.
        int minResult = INF;
        int fullMask = (1 << n) - 1;

        for (int val = 0; val <= 30; val++)
            minResult
                = Math.min(minResult, dp[val][fullMask]);

        return minResult >= INF ? -1 : minResult;
    }

    public static void main(String[] args)
    {
        int[] front = { 4, 1, 3 };
        int[] back = { 2, 1, 6 };

        GFG sol = new GFG();
        System.out.println(sol.minOperations(front, back));
    }
}
Python
def minOperations(front, back):
    n = len(front)
    INF = 10**9
    MAX_VAL = 30
    TOTAL_MASKS = 1 << n

    # dp[mask][val] stores the minimum number of adjacent swaps.
    dp = [[INF] * (MAX_VAL + 1) for _ in range(TOTAL_MASKS)]

    # Base case: no cards are selected.
    dp[0][0] = 0

    # Iterate through all subsets of placed cards.
    for mask in range(TOTAL_MASKS):
        # Compute prefix minimums.
        pref_min = [INF] * (MAX_VAL + 1)
        pref_min[0] = dp[mask][0]

        for v in range(1, MAX_VAL + 1):
            pref_min[v] = min(pref_min[v - 1], dp[mask][v])

        # Skip unreachable states.
        if pref_min[MAX_VAL] >= INF:
            continue

        # Target position of the next card.
        target_pos = mask.bit_count()

        # Try every unplaced card.
        for i in range(n):
            if not (mask & (1 << i)):
                # Determine the visible side at the target position.
                visible_val = (
                    front[i]
                    if (target_pos + i) % 2 == 0
                    else back[i]
                )

                # Find the minimum cost among valid previous values.
                prev_best = pref_min[visible_val]

                if prev_best >= INF:
                    continue

                # Count the swaps needed to place this card.
                cost = (mask >> (i + 1)).bit_count()

                next_mask = mask | (1 << i)

                # Update the DP state.
                if prev_best + cost < dp[next_mask][visible_val]:
                    dp[next_mask][visible_val] = prev_best + cost

    # Find the minimum operations for all cards.
    ans = min(dp[TOTAL_MASKS - 1][1:MAX_VAL + 1])

    return -1 if ans >= INF else ans


if __name__ == "__main__":
    front = [4, 1, 3]
    back = [2, 1, 6]

    print(minOperations(front, back))
C#
using System;

class GFG {
    public int minOperations(int[] front, int[] back)
    {
        int n = front.Length;
        const int INF = 999999999;

        // dp[val, mask] stores the minimum operations
        // needed for the selected cards with last visible
        // value val.
        int[, ] dp = new int[31, 1 << n];

        for (int i = 0; i <= 30; i++) {
            for (int j = 0; j < (1 << n); j++)
                dp[i, j] = INF;
        }

        // Initially, no cards are selected.
        dp[0, 0] = 0;

        // Iterate through all subsets of cards.
        for (int mask = 0; mask < (1 << n); mask++) {
            int pos = CountBits((uint)mask);

            // Try placing every unused card next.
            for (int k = 0; k < n; k++) {
                if ((mask & (1 << k)) != 0)
                    continue;

                // Determine the visible side at the new
                // position.
                int val = (pos + k) % 2 == 0 ? front[k]
                                             : back[k];

                int minVal = INF;

                // Find the minimum cost among valid
                // previous values.
                for (int l = 0; l <= val; l++)
                    minVal = Math.Min(minVal, dp[l, mask]);

                // Count the swaps needed to place this
                // card.
                int extra = CountBits(
                    (uint)(~mask & ((1 << k) - 1)));

                int newMask = mask | (1 << k);

                // Update the DP state.
                if (minVal != INF) {
                    dp[val, newMask] = Math.Min(
                        dp[val, newMask], minVal + extra);
                }
            }
        }

        // Find the minimum cost for the complete
        // arrangement.
        int minResult = INF;
        int fullMask = (1 << n) - 1;

        for (int val = 0; val <= 30; val++)
            minResult
                = Math.Min(minResult, dp[val, fullMask]);

        return minResult >= INF ? -1 : minResult;
    }

    // Count the number of set bits.
    static int CountBits(uint x)
    {
        int count = 0;
        while (x > 0) {
            count += (int)(x & 1);
            x >>= 1;
        }
        return count;
    }

    static void Main()
    {
        int[] front = { 4, 1, 3 };
        int[] back = { 2, 1, 6 };

        GFG sol = new GFG();
        Console.WriteLine(sol.minOperations(front, back));
    }
}
JavaScript
function minOperations(front, back)
{
    let n = front.length;
    const INF = 999999999;

    // dp[val][mask] stores the minimum operations needed
    // for the selected cards with last visible value val.
    let dp = Array.from({length : 31},
                        () => Array(1 << n).fill(INF));

    // Initially, no cards are selected.
    dp[0][0] = 0;

    // Iterate through all subsets of cards.
    for (let mask = 0; mask < (1 << n); mask++) {
        let pos = countBits(mask);

        // Try placing every unused card next.
        for (let k = 0; k < n; k++) {
            if (mask & (1 << k))
                continue;

            // Determine the visible side at the new
            // position.
            let val
                = (pos + k) % 2 === 0 ? front[k] : back[k];

            let minVal = INF;

            // Find the minimum cost among valid previous
            // values.
            for (let l = 0; l <= val; l++) {
                minVal = Math.min(minVal, dp[l][mask]);
            }

            // Count the swaps needed to place this card.
            let extra = countBits((~mask) & ((1 << k) - 1));

            let newMask = mask | (1 << k);

            // Update the DP state.
            if (minVal !== INF) {
                dp[val][newMask] = Math.min(
                    dp[val][newMask], minVal + extra);
            }
        }
    }

    // Find the minimum cost for the complete arrangement.
    let minResult = INF;
    let fullMask = (1 << n) - 1;

    for (let val = 0; val <= 30; val++) {
        minResult = Math.min(minResult, dp[val][fullMask]);
    }

    return minResult >= INF ? -1 : minResult;
}

// Count the number of set bits.
function countBits(x)
{
    let count = 0;
    while (x > 0) {
        count += x & 1;
        x >>>= 1;
    }
    return count;
}

// Driver Code
let front = [ 4, 1, 3 ];
let back = [ 2, 1, 6 ];

console.log(minOperations(front, back));

Output
1
Comment