Maximum GCD of K Partition Sums

Last Updated : 21 Jul, 2026

Given an array arr[] and an integer k, partition the array into exactly k non-empty contiguous subarrays such that the GCD of their sums is maximized. Return the maximum possible GCD.

Examples:

Input: k = 4, arr[] = [6, 7, 5, 27, 3]
Output: 3
Explanation: Since k = 4, you need to split the array into 4 subarrays. For optimal splitting, split the array into 4 subarrays as follows: [[6], [7, 5], [27], [3]]. Therefore, s1 = 6, s2 = 7 + 5 = 12, s3 = 27, s4 = 3. Hence, GCD(s1, s2, s3, s4) = GCD(6, 12, 27, 3) = 3 which is the maximum value of GCD that can be obtained.

Input: k = 2, arr[] = [1, 4, 5]
Output: 5
Explanation: Since k = 2, you need to split the array into 2 subarrays. For optimal splitting, split the array into 2 subarrays as follows: [[1, 4], [5]]. Therefore, s1 = 1 + 4 = 5, s2 = 5. Hence, GCD(s1, s2) = GCD(5, 5) = 5 which is the maximum value of GCD that can be obtained.

Try It Yourself
redirect icon

[Naive Approach] Brute Force Approach

The idea is to try every possible way to place the k - 1 cuts, since each unique set of cut positions defines a valid partition of the array into exactly k contiguous subarrays. Use prefix sums to compute each subarray sum in O(1) time and recursively maintain the GCD of the subarray sums, updating the maximum GCD obtained over all possible partitions.

  • Compute the prefix sum array of the given array.
  • Start from index 0 with k - 1 cuts remaining and an initial GCD of 0.
  • Recursively place the next cut at every valid position to generate all possible partitions.
  • For each chosen cut, compute the current subarray sum using prefix sums and update the running GCD.
  • When no cuts are left, form the last subarray, update the final GCD, and maximize the answer.
  • Return the maximum GCD obtained over all possible partitions.
C++
#include <iostream>
using namespace std;

// Recursively try all possible partitions
void backtrack(int idx, int cutsLeft, vector<int> &prefix, int currGCD, int &ans)
{
    int n = prefix.size() - 1;

    // No more cuts left, form the last subarray
    if (cutsLeft == 0)
    {
        int lastSum = prefix[n] - prefix[idx];
        currGCD = (currGCD == 0) ? lastSum : gcd(currGCD, lastSum);
        ans = max(ans, currGCD);
        return;
    }

    // Try placing the next cut at every possible position
    for (int i = idx + 1; i <= n - cutsLeft; i++)
    {
        int segmentSum = prefix[i] - prefix[idx];
        int newGCD = (currGCD == 0) ? segmentSum : gcd(currGCD, segmentSum);

        backtrack(i, cutsLeft - 1, prefix, newGCD, ans);
    }
}

int solve(int k, vector<int> &arr)
{
    int n = arr.size();

    // Compute prefix sums for O(1) subarray sum queries
    vector<int> prefix(n + 1, 0);
    for (int i = 0; i < n; i++)
        prefix[i + 1] = prefix[i] + arr[i];

    int ans = 0;

    // Start partitioning from index 0
    backtrack(0, k - 1, prefix, 0, ans);

    return ans;
}

int main()
{
    int k = 4;
    vector<int> arr = {6, 7, 5, 27, 3};

    // Print the maximum possible GCD
    cout << solve(k, arr) << endl;

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

public class GFG {

    // Recursively try all possible partitions
    static void backtrack(int idx, int cutsLeft,
                          int[] prefix, int currGCD,
                          int[] ans)
    {
        int n = prefix.length - 1;

        // No more cuts left, form the last subarray
        if (cutsLeft == 0) {
            int lastSum = prefix[n] - prefix[idx];
            currGCD = (currGCD == 0)
                          ? lastSum
                          : gcd(currGCD, lastSum);
            ans[0] = Math.max(ans[0], currGCD);
            return;
        }

        // Try placing the next cut at every possible
        // position
        for (int i = idx + 1; i <= n - cutsLeft; i++) {
            int segmentSum = prefix[i] - prefix[idx];
            int newGCD = (currGCD == 0)
                             ? segmentSum
                             : gcd(currGCD, segmentSum);

            backtrack(i, cutsLeft - 1, prefix, newGCD, ans);
        }
    }

    static int solve(int k, int[] arr)
    {
        int n = arr.length;

        // Compute prefix sums for O(1) subarray sum queries
        int[] prefix = new int[n + 1];
        for (int i = 0; i < n; i++)
            prefix[i + 1] = prefix[i] + arr[i];

        int[] ans = { 0 };

        // Start partitioning from index 0
        backtrack(0, k - 1, prefix, 0, ans);

        return ans[0];
    }

    static int gcd(int a, int b)
    {
        return (b == 0) ? a : gcd(b, a % b);
    }

    public static void main(String[] args)
    {

        int k = 4;
        int[] arr = { 6, 7, 5, 27, 3 };

        // Print the maximum possible GCD
        System.out.println(solve(k, arr));
    }
}
Python
from math import gcd

# Recursively try all possible partitions
def backtrack(idx, cuts_left, prefix, curr_gcd, ans):
    n = len(prefix) - 1

    # No more cuts left, form the last subarray
    if cuts_left == 0:
        last_sum = prefix[n] - prefix[idx]
        curr_gcd = last_sum if curr_gcd == 0 else gcd(curr_gcd, last_sum)
        ans[0] = max(ans[0], curr_gcd)
        return

    # Try placing the next cut at every possible position
    for i in range(idx + 1, n - cuts_left + 1):
        segment_sum = prefix[i] - prefix[idx]
        new_gcd = segment_sum if curr_gcd == 0 else gcd(curr_gcd, segment_sum)

        backtrack(i, cuts_left - 1, prefix, new_gcd, ans)


def solve(k, arr):
    n = len(arr)

    # Compute prefix sums for O(1) subarray sum queries
    prefix = [0] * (n + 1)
    for i in range(n):
        prefix[i + 1] = prefix[i] + arr[i]

    ans = [0]

    # Start partitioning from index 0
    backtrack(0, k - 1, prefix, 0, ans)

    return ans[0]

# Driver Code

if __name__ == "__main__":
    k = 4
    arr = [6, 7, 5, 27, 3]

    # Print the maximum possible GCD
    print(solve(k, arr))
C#
using System;

class GFG {
    
    // Recursively try all possible partitions
    static void Backtrack(int idx, int cutsLeft,
                          int[] prefix, int currGCD,
                          ref int ans)
    {
        int n = prefix.Length - 1;

        // No more cuts left, form the last subarray
        if (cutsLeft == 0) {
            int lastSum = prefix[n] - prefix[idx];
            currGCD = (currGCD == 0)
                          ? lastSum
                          : GCD(currGCD, lastSum);
            ans = Math.Max(ans, currGCD);
            return;
        }

        // Try placing the next cut at every possible
        // position
        for (int i = idx + 1; i <= n - cutsLeft; i++) {
            int segmentSum = prefix[i] - prefix[idx];
            int newGCD = (currGCD == 0)
                             ? segmentSum
                             : GCD(currGCD, segmentSum);

            Backtrack(i, cutsLeft - 1, prefix, newGCD,
                      ref ans);
        }
    }

    static int solve(int k, int[] arr)
    {
        int n = arr.Length;

        // Compute prefix sums for O(1) subarray sum
        // queries
        int[] prefix = new int[n + 1];
        for (int i = 0; i < n; i++)
            prefix[i + 1] = prefix[i] + arr[i];

        int ans = 0;

        // Start partitioning from index 0
        Backtrack(0, k - 1, prefix, 0, ref ans);

        return ans;
    }

    static int GCD(int a, int b)
    {
        return (b == 0) ? a : GCD(b, a % b);
    }

    static void Main()
    {
        int k = 4;
        int[] arr = { 6, 7, 5, 27, 3 };

        // Print the maximum possible GCD
        Console.WriteLine(solve(k, arr));
    }
}
JavaScript
// Compute GCD of two numbers
function gcd(a, b) { return b === 0 ? a : gcd(b, a % b); }

// Recursively try all possible partitions
function backtrack(idx, cutsLeft, prefix, currGCD, ans)
{
    const n = prefix.length - 1;

    // No more cuts left, form the last subarray
    if (cutsLeft === 0) {
        const lastSum = prefix[n] - prefix[idx];
        currGCD = (currGCD === 0) ? lastSum
                                  : gcd(currGCD, lastSum);
        ans.value = Math.max(ans.value, currGCD);
        return;
    }

    // Try placing the next cut at every possible position
    for (let i = idx + 1; i <= n - cutsLeft; i++) {
        const segmentSum = prefix[i] - prefix[idx];
        const newGCD = (currGCD === 0)
                           ? segmentSum
                           : gcd(currGCD, segmentSum);

        backtrack(i, cutsLeft - 1, prefix, newGCD, ans);
    }
}

function solve(k, arr)
{
    const n = arr.length;

    // Compute prefix sums for O(1) subarray sum queries
    const prefix = new Array(n + 1).fill(0);
    for (let i = 0; i < n; i++)
        prefix[i + 1] = prefix[i] + arr[i];

    const ans = {value : 0};

    // Start partitioning from index 0
    backtrack(0, k - 1, prefix, 0, ans);

    return ans.value;
}

// Driver Code

const k = 4;
const arr = [ 6, 7, 5, 27, 3 ];

// Print the maximum possible GCD
console.log(solve(k, arr));

Output
3

Time Complexity: O(C(n − 1, k − 1) * k * log(S)), where S is the total sum of the array.
Auxiliary Space: O(n + k)

[Expected Approach] Using Divisors and Prefix Sum

Instead of checking every possible partition, observe that the GCD of the subarray sums must divide the total sum of the array. Therefore, we only need to examine the divisors of the total sum and use prefix sums to efficiently determine whether the array can be partitioned into at least k contiguous subarrays whose sums are all divisible by a chosen divisor.

  • Compute the total sum of the array.
  • Find all divisors of the total sum and sort them in descending order.
  • Compute the prefix sum array.
  • For each divisor g, count the number of prefix sums divisible by g.
  • If at least k prefix sums are divisible by g, return g as it is the maximum possible GCD.
  • If no larger divisor satisfies the condition, return 1.
C++
#include <bits/stdc++.h>
using namespace std;

int solve(int k, vector<int> &arr)
{
    int n = arr.size();
    int totalSum = 0;

    // Compute the total sum of the array
    for (int x : arr)
        totalSum += x;

    vector<int> divisors;

    // Find all divisors of the total sum
    for (int i = 1; i * i <= totalSum; i++)
    {
        if (totalSum % i == 0)
        {
            divisors.push_back(i);

            if (i != totalSum / i)
                divisors.push_back(totalSum / i);
        }
    }

    // Check larger divisors first
    sort(divisors.rbegin(), divisors.rend());

    // Compute prefix sums
    for (int i = 1; i < n; i++)
        arr[i] += arr[i - 1];

    // Check each divisor
    for (int g : divisors)
    {
        int cnt = 0;

        // Count prefix sums divisible by the current divisor
        for (int sum : arr)
        {
            if (sum % g == 0)
                cnt++;
        }

        // If at least k valid segment endings exist,
        // we can partition the array into k subarrays
        if (cnt >= k)
            return g;
    }

    return 1;
}

int main()
{
    int k = 4;
    vector<int> arr = {6, 7, 5, 27, 3};

    // Print the maximum possible GCD
    cout << solve(k, arr) << endl;

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

public class GFG {
    static int solve(int k, int[] arr)
    {
        int n = arr.length;
        int totalSum = 0;

        // Compute the total sum of the array
        for (int x : arr)
            totalSum += x;

        ArrayList<Integer> divisors = new ArrayList<>();

        // Find all divisors of the total sum
        for (int i = 1; i * i <= totalSum; i++) {
            if (totalSum % i == 0) {
                divisors.add(i);

                if (i != totalSum / i)
                    divisors.add(totalSum / i);
            }
        }

        // Check larger divisors first
        divisors.sort(Collections.reverseOrder());

        // Compute prefix sums
        for (int i = 1; i < n; i++)
            arr[i] += arr[i - 1];

        // Check each divisor
        for (int g : divisors) {

            int cnt = 0;

            // Count prefix sums divisible by the current
            // divisor
            for (int sum : arr) {
                if (sum % g == 0)
                    cnt++;
            }

            // If at least k valid segment endings exist,
            // we can partition the array into k subarrays
            if (cnt >= k)
                return g;
        }

        return 1;
    }

    public static void main(String[] args)
    {
        int k = 4;
        int[] arr = { 6, 7, 5, 27, 3 };

        // Print the maximum possible GCD
        System.out.println(solve(k, arr));
    }
}
Python
def solve(k, arr):
    n = len(arr)
    total_sum = sum(arr)

    divisors = []

    # Find all divisors of the total sum
    i = 1
    while i * i <= total_sum:
        if total_sum % i == 0:
            divisors.append(i)

            if i != total_sum // i:
                divisors.append(total_sum // i)

        i += 1

    # Check larger divisors first
    divisors.sort(reverse=True)

    # Compute prefix sums
    for i in range(1, n):
        arr[i] += arr[i - 1]

    # Check each divisor
    for g in divisors:

        cnt = 0

        # Count prefix sums divisible by the current divisor
        for prefix_sum in arr:
            if prefix_sum % g == 0:
                cnt += 1

        # If at least k valid segment endings exist,
        # we can partition the array into k subarrays
        if cnt >= k:
            return g

    return 1

# Driver Code

if __name__ == "__main__":
    k = 4
    arr = [6, 7, 5, 27, 3]

    # Print the maximum possible GCD
    print(solve(k, arr))
C#
using System;
using System.Collections.Generic;

class GFG {
    static int solve(int k, int[] arr)
    {
        int n = arr.Length;
        int totalSum = 0;

        // Compute the total sum of the array
        foreach(int x in arr) totalSum += x;

        List<int> divisors = new List<int>();

        // Find all divisors of the total sum
        for (int i = 1; i * i <= totalSum; i++) {
            if (totalSum % i == 0) {
                divisors.Add(i);

                if (i != totalSum / i)
                    divisors.Add(totalSum / i);
            }
        }

        // Check larger divisors first
        divisors.Sort((a, b) => b.CompareTo(a));

        // Compute prefix sums
        for (int i = 1; i < n; i++)
            arr[i] += arr[i - 1];

        // Check each divisor
        foreach(int g in divisors)
        {
            int cnt = 0;

            // Count prefix sums divisible by the current
            // divisor
            foreach(int prefixSum in arr)
            {
                if (prefixSum % g == 0)
                    cnt++;
            }

            // If at least k valid segment endings exist,
            // we can partition the array into k subarrays
            if (cnt >= k)
                return g;
        }

        return 1;
    }

    static void Main()
    {
        int k = 4;
        int[] arr = { 6, 7, 5, 27, 3 };

        // Print the maximum possible GCD
        Console.WriteLine(solve(k, arr));
    }
}
JavaScript
function solve(k, arr)
{
    const n = arr.length;
    let totalSum = 0;

    // Compute the total sum of the array
    for (const x of arr)
        totalSum += x;

    const divisors = [];

    // Find all divisors of the total sum
    for (let i = 1; i * i <= totalSum; i++) {
        if (totalSum % i === 0) {
            divisors.push(i);

            if (i !== totalSum / i)
                divisors.push(totalSum / i);
        }
    }

    // Check larger divisors first
    divisors.sort((a, b) => b - a);

    // Compute prefix sums
    for (let i = 1; i < n; i++)
        arr[i] += arr[i - 1];

    // Check each divisor
    for (const g of divisors) {

        let cnt = 0;

        // Count prefix sums divisible by the current
        // divisor
        for (const prefixSum of arr) {
            if (prefixSum % g === 0)
                cnt++;
        }

        // If at least k valid segment endings exist,
        // we can partition the array into k subarrays
        if (cnt >= k)
            return g;
    }

    return 1;
}

// Driver Code

const k = 4;
const arr = [ 6, 7, 5, 27, 3 ];

// Print the maximum possible GCD
console.log(solve(k, arr));

Output
3

Time Complexity: O(sqrt(S) + d * log(d) + n * d) where n = size of the array, S = total sum of the array, and d = number of divisors of S.
Auxiliary Space: O(d)

Comment