Talentd
FleetCode
Make and follow sheets.Sign in

Balanced K-Factor Decomposition solution and explanation

MediumBacktrackingMathNumber Theory3 min read40.2% acceptanceUpdated 24 Mar 2026
Balanced K-Factor Decomposition | LeetCode 3669 | Weekly Contest 465Sanyam IIT Guwahati · 12:56 · 1K viewsWatch on YouTube
9 more videos on this problem
  1. Balanced K-Factor Decomposition | LeetCode 3669 | Weekly Contest 465Sanyam IIT Guwahati · 12:56 · 1K views
  2. Balanced K-Factor Decomposition | Weekly Contest 465 | Q2ExpertFunda · 11:55 · 1K views
  3. Leetcode 3669 | Balanced K-Factor Decomposition | weekly contest 465CodeWithMeGuys · 14:53 · 516 views
  4. 3669. Balanced K-Factor Decomposition (Leetcode Medium)Programming Live with Larry · 15:57 · 496 views
  5. 3669. Balanced K-Factor Decomposition | Leetcode Weekly Contest 465Break and Build · 23:51 · 394 views
  6. Leetcode 3669 | Balanced K-Factor Decomposition Detailed Explanation with Dry run | Weekly 465 Q2Samrat Bhardwaj · 31:49 · 371 views
  7. Balanced K-Factor Decomposition | Weekly Contest 465 | Q2. Balanced K-Factor Decomposition|backtrackCode Thoughts · 9:50 · 228 views
  8. 【每日一题】LeetCode 3669. Balanced K-Factor DecompositionHuifeng Guan · 15:51 · 200 views
  9. Weekly Contest 465 Q2 | LeetCode 3669 – Balanced K-Factor Decomposition | Java Solution + Dry Run Study Placement · 22:13 · 195 views
  10. Leetcode 3669 | Balanced K Factor Decomposition Weekly Contest 465 | Hindi | JavaKernel Queen 👑 · 11:31 · 123 views

The problem

Given two integers n and k, split the number n into exactly k positive integers such that the product of these integers is equal to n.

Return any one split in which the maximum difference between any two numbers is minimized. You may return the result in any order.

Example 1:

Input: n = 100, k = 2

Output: [10,10]

Explanation:

The split [10, 10] yields 10 * 10 = 100 and a max-min difference of 0, which is minimal.

Example 2:

Input: n = 44, k = 3

Output: [2,2,11]

Explanation:

  • Split [1, 1, 44] yields a difference of 43
  • Split [1, 2, 22] yields a difference of 21
  • Split [1, 4, 11] yields a difference of 10
  • Split [2, 2, 11] yields a difference of 9

Therefore, [2, 2, 11] is the optimal split with the smallest difference 9.

Constraints:

  • 4 <= n <= 105
  • 2 <= k <= 5
  • k is strictly less than the total number of positive divisors of n.

Approach overview

Problem Overview: You are given an integer n and a target count k. The task is to decompose n into exactly k integer factors whose product equals n while keeping the factors as balanced as possible (no unnecessary skew toward very small or very large factors).

Approach 1: Exhaustive Factor Combination (Brute Force) (Time: O(d^k), Space: O(k))

Start by generating all divisors of n. Then try every possible combination of k divisors whose product equals n. A recursive search multiplies chosen factors and tracks how many have been used so far. If the product exceeds n or the count exceeds k, the branch stops early. This method is straightforward but explores many redundant combinations, especially when n has many divisors.

Approach 2: Backtracking with Ordered Factors and Pruning (Time: O(d^k) worst case, typically much smaller; Space: O(k))

A better strategy uses backtracking combined with properties from number theory. Instead of trying all divisor permutations, enforce a non‑decreasing order of factors. At each recursion step, iterate through divisors starting from the previous factor to avoid duplicate arrangements. Divide the remaining product by the chosen factor and continue searching. If the remaining value cannot produce enough factors to reach k, prune the branch immediately.

The key insight is that factor combinations behave like multiplicative partitions. Ordering the factors and shrinking the remaining product drastically reduces the search space. This approach also keeps factors naturally balanced because extremely small factors quickly force large remaining values, which fail the pruning checks.

Precomputing divisors of n in O(√n) time further speeds up the recursion. Each step only explores valid divisors of the remaining number, making the search practical even when the theoretical worst case is exponential.

Recommended for interviews: Interviewers expect the optimized backtracking approach. The brute force solution shows that you understand factor generation, but the ordered recursion with pruning demonstrates deeper understanding of math structure and efficient search space reduction.

Solution

mx = 10**5 + 1
g = [[] for _ in range(mx)]
for i in range(1, mx):
    for j in range(i, mx, i):
        g[j].append(i)


class Solution:
    def minDifference(self, n: int, k: int) -> List[int]:
        def dfs(i: int, x: int, mi: int, mx: int):
            if i == 0:
                nonlocal cur, ans
                d = max(mx, x) - min(mi, x)
                if d < cur:
                    cur = d
                    path[i] = x
                    ans = path[:]
                return
            for y in g[x]:
                path[i] = y
                dfs(i - 1, x // y, min(mi, y), max(mx, y))

        ans = None
        path = [0] * k
        cur = inf
        dfs(k - 1, n, inf, 0)
        return ans
class Solution {
    static final int MX = 100_001;
    static List<Integer>[] g = new ArrayList[MX];

    static {
        for (int i = 0; i < MX; i++) {
            g[i] = new ArrayList<>();
        }
        for (int i = 1; i < MX; i++) {
            for (int j = i; j < MX; j += i) {
                g[j].add(i);
            }
        }
    }

    private int cur;
    private int[] ans;
    private int[] path;

    public int[] minDifference(int n, int k) {
        cur = Integer.MAX_VALUE;
        ans = null;
        path = new int[k];
        dfs(k - 1, n, Integer.MAX_VALUE, 0);
        return ans;
    }

    private void dfs(int i, int x, int mi, int mx) {
        if (i == 0) {
            int d = Math.max(mx, x) - Math.min(mi, x);
            if (d < cur) {
                cur = d;
                path[i] = x;
                ans = path.clone();
            }
            return;
        }
        for (int y : g[x]) {
            path[i] = y;
            dfs(i - 1, x / y, Math.min(mi, y), Math.max(mx, y));
        }
    }
}
class Solution {
public:
    static const int MX = 100001;
    static vector<vector<int>> g;

    vector<int> ans;
    vector<int> path;
    int cur;

    vector<int> minDifference(int n, int k) {
        if (g.empty()) {
            g.resize(MX);
            for (int i = 1; i < MX; i++) {
                for (int j = i; j < MX; j += i) {
                    g[j].push_back(i);
                }
            }
        }

        cur = INT_MAX;
        ans.clear();
        path.assign(k, 0);

        dfs(k - 1, n, INT_MAX, 0);
        return ans;
    }

private:
    void dfs(int i, int x, int mi, int mx) {
        if (i == 0) {
            int d = max(mx, x) - min(mi, x);
            if (d < cur) {
                cur = d;
                path[i] = x;
                ans = path;
            }
            return;
        }
        for (int y : g[x]) {
            path[i] = y;
            dfs(i - 1, x / y, min(mi, y), max(mx, y));
        }
    }
};

vector<vector<int>> Solution::g;
const MX = 100001;
const g: number[][] = Array.from({ length: MX }, () => []);
for (let i = 1; i < MX; i++) {
    for (let j = i; j < MX; j += i) {
        g[j].push(i);
    }
}

function minDifference(n: number, k: number): number[] {
    let cur = Number.MAX_SAFE_INTEGER;
    let ans: number[] | null = null;
    const path: number[] = Array(k).fill(0);

    function dfs(i: number, x: number, mi: number, mx: number): void {
        if (i === 0) {
            const d = Math.max(mx, x) - Math.min(mi, x);
            if (d < cur) {
                cur = d;
                path[i] = x;
                ans = [...path];
            }
            return;
        }
        for (const y of g[x]) {
            path[i] = y;
            dfs(i - 1, Math.floor(x / y), Math.min(mi, y), Math.max(mx, y));
        }
    }

    dfs(k - 1, n, Number.MAX_SAFE_INTEGER, 0);
    return ans ?? [];
}
const MX = 100001

var g [][]int

func init() {
	g = make([][]int, MX)
	for i := 1; i < MX; i++ {
		for j := i; j < MX; j += i {
			g[j] = append(g[j], i)
		}
	}
}

var (
	cur  int
	ans  []int
	path []int
)

func minDifference(n int, k int) []int {
	cur = math.MaxInt32
	ans = nil
	path = make([]int, k)
	dfs(k-1, n, math.MaxInt32, 0)
	return ans
}

func dfs(i, x, mi, mx int) {
	if i == 0 {
		d := max(mx, x) - min(mi, x)
		if d < cur {
			cur = d
			path[i] = x
			ans = slices.Clone(path)
		}
		return
	}
	for _, y := range g[x] {
		path[i] = y
		dfs(i-1, x/y, min(mi, y), max(mx, y))
	}
}
Try this approach in the editor

Complexity analysis

ApproachTimeSpaceWhen to Use
Exhaustive Factor CombinationO(d^k)O(k)Useful for understanding multiplicative partitions or when n has very few divisors
Backtracking with Ordered Factors and PruningO(d^k) worst caseO(k)General solution. Avoids duplicate permutations and prunes impossible branches early

Frequently asked questions

Is Balanced K-Factor Decomposition easy or hard?

Balanced K-Factor Decomposition is generally considered a medium difficulty problem. The core math is simple, but designing an efficient recursive search with pruning requires familiarity with backtracking and divisor-based reasoning.

Balanced K-Factor Decomposition Python/Java solution

The typical implementation uses a recursive backtracking function that tracks the remaining product and the number of factors chosen. Python, Java, C++, Go, and TypeScript versions follow the same structure: compute divisors, enforce ordered selection, and prune branches when the remaining value cannot reach k factors.

How to solve Balanced K-Factor Decomposition in O(n)?

An O(n) solution is generally not possible because the task requires exploring combinations of factors. Even with pruning, the algorithm must consider multiple multiplicative partitions of n. Efficient solutions rely on backtracking with divisor filtering rather than linear-time processing.

What is the best approach for Balanced K-Factor Decomposition?

Backtracking with ordered factors and pruning is the most practical approach. Generate divisors of n, then recursively build k factors while ensuring the sequence is non-decreasing. This avoids duplicate permutations and cuts branches where the remaining product cannot produce enough factors.

Is Balanced K-Factor Decomposition asked at Google/Amazon/Meta?

Factor decomposition and multiplicative partition problems appear in interviews at large tech companies because they test recursion, pruning, and number theory reasoning. Variants involving factor combinations or product partitions have been reported in interviews at companies like Amazon and Google.

What data structure is used in Balanced K-Factor Decomposition?

The solution primarily uses recursion with a dynamic list or stack to store the current factor path. A precomputed list of divisors helps limit candidate values. The algorithm is driven by backtracking rather than specialized data structures.

What is the time complexity of Balanced K-Factor Decomposition?

The worst-case time complexity is exponential, typically expressed as O(d^k) where d is the number of divisors of n. Each recursive level tries valid divisors of the remaining value. Space complexity is O(k) due to the recursion stack and factor list.

Comments

Now write it yourselfClose this page and solve Balanced K-Factor Decomposition in the editor, without looking.
Practise Balanced K-Factor Decomposition