GeeksforGeeks App
Open App
Browser
Continue

# Divide and Conquer Optimization in Dynamic Programming

Dynamic programming (DP) is arguably the most important tool in a competitive programmer’s repertoire. There are several optimizations in DP that reduce the time complexity of standard DP procedures by a linear factor or more, such as Knuth’s optimization, Divide and Conquer optimization, the Convex Hull Trick, etc. They are, of paramount importance for advanced competitive programming, such as at the level of olympiads. In this article, we will discover the divide and conquer optimization, NOT to be confused with the divide and conquer algorithm to solve problems.

## Divide and Conquer Optimization Criteria:

The divide and conquer optimization can be used for problems with a dp transition of the following form –

dp[i][j] = min1≤k<j (dp[i-1][k-1] + cost[k][j])

Further, the cost function must satisfy the quadrangle inequality (QI), i.e.,

cost(a, c) + cost(b, d) ≤ cost(a, d) + cost(b, c) for all a ≤ b ≤ c ≤ d.

## Divide and Conquer Optimization Technique:

The sub-optimal approach to solve any problem with a dynamic programming transition of the form given above would iterate through all possible values of k < j for each transition. Then, if the problem constraints give 1 ≤ i ≤ m and 1 ≤ j ≤ n, the algorithm will take O(mn2) time.

The key to the optimization is the following:

• Like in Knuth’s optimization, define the function opt(i, j), the minimum (or maximum, doesn’t matter) value of k for which dp[i][j] takes its minimum value. Then, we have the following relation:

opt[i][j] ≤ opt[i][j+1], where

opt[i][j] = argmink<j(dp[i-1][k] + cost[k][j])

Now, suppose we compute opt[i][j] for some i and j. Then, we also know that opt[i][p] ≤ opt[i][j] for all p < j. The sub-optimal solution would involve looping for each j, through all possible values of k for any fixed i. The optimization itself is as follows:

• Loop through the values of i, and first compute dp[i][j] and opt[i][j] for j = n/2, for the current i.  This is possible as at the time of processing, we know all the values in the dp table for dp[i-1][k] for all k ≤ n, due to the structure of the loop.
• Now, calculate dp[i][n/4] and dp[i][3n/4], knowing that opt[i][n/4] ≤ opt[i][n/2] and opt[i][n/2] ≤ opt[i][3n/4]
• We recursively call this solve function, keeping track of the lower and upper bounds for opt[i][j] for some i and the current j. For instance, when calculating dp[i][5n/8], we know that opt[i][n/2] ≤ opt[i][5n/8] ≤ opt[i][3n/4]

The algorithm is faster by a linear factor as we don’t have to loop through all values of k, and a logarithmic factor is added due to the recursive nature of this algorithm. The time complexity is thus O(m * n * (log n)).

The generic code for this approach is given below It uses a recursive approach, which is the simplest to implement given the structure of the solution.

## C++

 `// C++ code for generic approach``// of the divide and conquer optimization` `#include ``using` `namespace` `std;` `const` `int` `MAX_M = 10005;``const` `int` `MAX_N = 1005;``int` `N, M, dp[MAX_M][MAX_N], cost[MAX_M][MAX_M];` `// Function to perform the decide and conquer``void` `divide(``int` `l, ``int` `r, ``int` `optl, ``int` `optr, ``int` `i)``{``    ``if` `(l > r)``        ``return``;` `    ``// Find middle value``    ``int` `mid = (l + r) >> 1;` `    ``// Store the minimum cost and opt(i, j)``    ``pair<``int``, ``int``> best = { INT_MAX, -1 };` `    ``// Find the value of the best cost and opt(i, j)``    ``for` `(``int` `k = optl; k < min(mid, optr); k++)``        ``best = min(``            ``best,``            ``{ (k ? dp[i - 1][k] : 0) + cost[k][mid], k });` `    ``// Store the minimum cost in the dp array``    ``dp[i][mid] = best.first;``    ``int` `opt = best.second;` `    ``// Recursively call the divide function``    ``// to fill the other dp states``    ``divide(l, mid - 1, optl, opt, i);``    ``divide(mid + 1, r, opt, optr, i);``}` `void` `solve()``{``    ``// Initial state of the dp table``    ``// Normally table is initialized for j=0``    ``// or j=1 depending on problem statement``    ``for` `(``int` `i = 0; i < N; i++)``        ``dp[0][i] = cost[0][i];` `    ``// Fill in the dp array``    ``// with the divide function``    ``for` `(``int` `i = 1; i < M; i++)``        ``divide(0, N - 1, 0, N - 1, i);` `    ``cout << dp[M - 1][N - 1] << endl;``}` `int` `main()``{``    ``// Take the required inputs``    ``solve();``    ``return` `0;``}`

## Java

 `// Java code for generic approach``// of the divide and conquer optimization` `import` `java.util.Arrays;` `class` `GFG {``    ``static` `final` `int` `MAX_M = ``10005``;``    ``static` `final` `int` `MAX_N = ``1005``;``    ``static` `int` `N, M, dp[][] = ``new` `int``[MAX_M][MAX_N];``    ``static` `int` `cost[][] = ``new` `int``[MAX_M][MAX_M];` `    ``// Function to perform the decide and conquer``    ``static` `void` `divide(``int` `l, ``int` `r, ``int` `optl, ``int` `optr,``                       ``int` `i)``    ``{``        ``if` `(l > r)``            ``return``;` `        ``// Find middle value``        ``int` `mid = (l + r) >> ``1``;` `        ``// Store the minimum cost and opt(i, j)``        ``Pair best = ``new` `Pair(Integer.MAX_VALUE, -``1``);` `        ``// Find the value of the best cost and opt(i, j)``        ``for` `(``int` `k = optl; k < Math.min(mid, optr); k++)``            ``best = min(best,``                       ``new` `Pair((k != ``0` `? dp[i - ``1``][k] : ``0``)``                                    ``+ cost[k][mid],``                                ``k));` `        ``// Store the minimum cost in the dp array``        ``dp[i][mid] = best.first;``        ``int` `opt = best.second;` `        ``// Recursively call the divide function``        ``// to fill the other dp states``        ``divide(l, mid - ``1``, optl, opt, i);``        ``divide(mid + ``1``, r, opt, optr, i);``    ``}` `    ``// Utility function to find minimum``    ``// of two pairs``    ``static` `Pair min(Pair a, Pair b)``    ``{``        ``return` `a.first < b.first ? a : b;``    ``}` `    ``static` `void` `solve()``    ``{``        ``// Initial state of the dp table``        ``// Normally table is initialized for j=0``        ``// or j=1 depending on problem statement``        ``for` `(``int` `i = ``0``; i < N; i++)``            ``dp[``0``][i] = cost[``0``][i];` `        ``// Fill in the dp array``        ``// with the divide function``        ``for` `(``int` `i = ``1``; i < M; i++)``            ``divide(``0``, N - ``1``, ``0``, N - ``1``, i);` `        ``System.out.println(dp[M - ``1``][N - ``1``]);``    ``}` `    ``// Driver code``    ``public` `static` `void` `main(String[] args)``    ``{``        ``// Take the required inputs``        ``solve();``    ``}` `    ``// A pair of integers``    ``static` `class` `Pair {``        ``int` `first, second;``        ``public` `Pair(``int` `a, ``int` `b)``        ``{``            ``first = a;``            ``second = b;``        ``}``    ``}``}``// This code is contributed by akashish__``// Output will be memory limit exceeded if M and N are not``// set.`

## Python3

 `# Python code for generic approach``# of the divide and conquer optimization``MAX_M ``=` `10005``MAX_N ``=` `1005``N, M ``=` `None``, ``None``dp ``=` `[[``0` `for` `_ ``in` `range``(MAX_N)] ``for` `_ ``in` `range``(MAX_M)]``cost ``=` `[[``0` `for` `_ ``in` `range``(MAX_M)] ``for` `_ ``in` `range``(MAX_M)]` `# Function to perform the decide and conquer`  `def` `divide(l, r, optl, optr, i):``    ``if` `(l > r):``        ``return` `    ``# Find middle value``    ``mid ``=` `(l ``+` `r) >> ``1` `    ``# Store the minimum cost and opt(i, j)``    ``best ``=` `{``"first"``: ``float``(``"inf"``), ``"second"``: ``-``1``}` `    ``# Find the value of the best cost and opt(i, j)``    ``for` `k ``in` `range``(optl, ``min``(mid, optr)):``        ``best[``"first"``] ``=` `min``(``            ``best[``"first"``], (dp[i``-``1``][k] ``if` `k ``else` `0``) ``+` `cost[k][mid], k)` `    ``# Store the minimum cost in the dp array``    ``dp[i][mid] ``=` `best[``"first"``]``    ``opt ``=` `best[``"second"``]` `    ``# Recursively call the divide function``    ``# to fill the other dp states``    ``divide(l, mid ``-` `1``, optl, opt, i)``    ``divide(mid ``+` `1``, r, opt, optr, i)`  `def` `solve():``    ``# Initial state of the dp table``    ``# Normally table is initialized for j=0``    ``# or j=1 depending on problem statement``    ``for` `i ``in` `range``(N):``        ``dp[``0``][i] ``=` `cost[``0``][i]` `    ``# Fill in the dp array``    ``# with the divide function``    ``for` `i ``in` `range``(``1``, M):``        ``divide(``0``, N ``-` `1``, ``0``, N ``-` `1``, i)` `    ``# M=1,N=1;``    ``print``(dp[M``-``1``][N``-``1``])`  `# Take the required inputs``solve()` `# This code is contributed by akashish__``# Output will be memory limit exceeded if M and N are not set.`

## C#

 `using` `System;` `public` `class` `GFG``{``    ``static` `readonly` `int` `MAX_M = 10005;``    ``static` `readonly` `int` `MAX_N = 1005;``    ``static` `int` `N, M;``    ``static` `int``[][] dp = ``new` `int``[MAX_M][];``    ``static` `int``[][] cost = ``new` `int``[MAX_M][];` `    ``// A pair of integers``    ``class` `Pair``    ``{``        ``public` `int` `first, second;` `        ``public` `Pair(``int` `a, ``int` `b)``        ``{``            ``first = a;``            ``second = b;``        ``}``    ``}` `    ``// Function to perform the divide and conquer``    ``static` `void` `Divide(``int` `l, ``int` `r, ``int` `optl, ``int` `optr, ``int` `i)``    ``{``        ``if` `(l > r)``            ``return``;` `        ``// Find middle value``        ``int` `mid = (l + r) >> 1;` `        ``// Store the minimum cost and opt(i, j)``        ``Pair best = ``new` `Pair(``int``.MaxValue, -1);` `        ``// Find the value of the best cost and opt(i, j)``        ``for` `(``int` `k = optl; k < Math.Min(mid, optr); k++)``            ``best = Min(best, ``new` `Pair((k != 0 ? dp[i - 1][k] : 0) + cost[k][mid], k));` `        ``// Store the minimum cost in the dp array``        ``dp[i][mid] = best.first;``        ``int` `opt = best.second;` `        ``// Recursively call the divide function``        ``// to fill the other dp states``        ``Divide(l, mid - 1, optl, opt, i);``        ``Divide(mid + 1, r, opt, optr, i);``    ``}` `    ``// Utility function to find minimum of two pairs``    ``static` `Pair Min(Pair a, Pair b)``    ``{``        ``return` `a.first < b.first ? a : b;``    ``}` `    ``static` `void` `Solve()``    ``{``        ``// Initial state of the dp table``        ``// Normally table is initialized for j=0``        ``// or j=1 depending on problem statement``        ``for` `(``int` `i = 0; i < N; i++)``            ``dp[0][i] = cost[0][i];` `        ``// Fill in the dp array``        ``// with the divide function``        ``for` `(``int` `i = 1; i < M; i++)``            ``Divide(0, N - 1, 0, N - 1, i);` `        ``Console.WriteLine(dp[M - 1][N - 1]);``    ``}` `    ``// Driver code``    ``public` `static` `void` `Main()``    ``{``        ``// Take the required inputs``        ``N = 3;``        ``M = 2;``        ``dp = ``new` `int``[M][];``        ``cost = ``new` `int``[M][];` `        ``for` `(``int` `i = 0; i < M; i++)``        ``{``            ``dp[i] = ``new` `int``[N];``            ``cost[i] = ``new` `int``[N];``        ``}` `        ``cost[0][0] = 4;``        ``cost[0][1] = 6;``        ``cost[0][2] = 8;``        ``cost[1][0] = 9;``        ``cost[1][1] = 2;``        ``cost[1][2] = 3;` `        ``Solve();``    ``}``}`

## Javascript

 `// Javascript code for generic approach``// of the divide and conquer optimization``const MAX_M = 10005;``const MAX_N = 1005;``let N, M;``const dp = ``new` `Array(MAX_M).fill(0).map(() => ``new` `Array(MAX_N).fill(0));``const cost = ``new` `Array(MAX_M).fill(0).map(() => ``new` `Array(MAX_M).fill(0));` `// Function to perform the decide and conquer``function` `divide(l, r,optl, optr, i)``{``    ``if` `(l > r) ``      ``return``;` `    ``// Find middle value``    ``let mid = (l + r) >> 1;``  ` `    ``// Store the minimum cost and opt(i, j)``    ``let best = {``"first"``:INT_MAX, ``"second"``:-1};` `    ``// Find the value of the best cost and opt(i, j)``    ``for` `(let k = optl; k < Math.min(mid, optr); k++)``        ``best.first = Math.min(best.first, ((k ? dp[i-1][k] : 0)``                          ``+ cost[k][mid], k));` `    ``// Store the minimum cost in the dp array``    ``dp[i][mid] = best.first;``    ``let opt = best.second;` `    ``// Recursively call the divide function``    ``// to fill the other dp states``    ``divide(l, mid - 1, optl, opt, i);``    ``divide(mid + 1, r, opt, optr, i);``}`  `function` `solve()``{``    ``// Initial state of the dp table``    ``// Normally table is initialized for j=0``    ``// or j=1 depending on problem statement``    ``for` `(let i = 0; i < N; i++)``        ``dp[0][i] = cost[0][i];` `    ``// Fill in the dp array``    ``// with the divide function``    ``for` `(let i = 1; i < M; i++)``        ``divide(0, N - 1, 0, N - 1, i);``    ` `    ``// M=1,N=1; // if we donot set any value of M and N it'll return undefined``    ``console.log(dp[M-1][N-1]);``}` `// Take the required inputs``solve();``// contributed by akashish__``// Output will be memory limit exceeded`

Time Complexity: O(M*N2*log2N).

Space Complexity: O(M*N) as 2d array dp has been created.

## Proof of Correctness of Divide and Conquer Optimization:

To prove the correctness of this algorithm, we only need to prove the inequality –

opt[i][j] ≤ opt[i][j+1]

Follow the below section for proof of correctness:

Assumptions: If cost(i, j) satisfies the Quadrangle Inequality, then dp[i][j] also satisfies the inequality.
Now, consider the following setup –

• We have some indices 1 ≤ p ≤ q ≤ j and a separate fixed i
• Let dpk[i][j] = cost[k][i] + dp[k-1][j-1].

If we can show that –

dpp[i][j] ≥ dpq[i][j] ⇒ dpp[i][j+1] ≥ dpq[i][j+1]

then setting q = opt[i][j], it will be clear that opt[i][j+1] will be at least as much as opt[i][j], due to the implication of the above inequality for all indices p less than opt[i][j]. This will prove that opt[i][j-1] ≤ opt[i][j].

Prrof:

From the Quadrangle inequality on the dp array we get –

cost(p, j) + cost(q, j+1) ≤ cost(p, j+1) + cost(q, j)
⇒ (dp[i-1, p] + cost(p, j)) + (dp[i-1, q] + cost(q, j+1)) ≤ (dp[i-1, p] + cost(p, j+1)) + (dp[j-1, q] + cost(q, j))
⇒ dpp[i][j] + dpq[i][j+1] ≤ dpp[i][j+1] + dpq[i][j]
⇒ dpp[i][j] – dpq[i][j] ≤ dpp[i][j+1] – dpq[i][j+1]

dpp[i][j] ≥ dpq[i][j]
⇒ 0 ≤ dpp[i][j] – dpq[i][j] ≤ dpp[i][j+1] – dpq[i][j+1]
⇒ dpp[i][j+1] ≥ dpq[i][j+1]

This completes the proof dpp[i][j] ≥ dpq[i][j] ⇒ dpp[i][j+1] ≥ dpq[i][j+1]

## Examples to Show Working of Divide and Conquer Optimization:

Given an array arr[] of N elements, the task is to divide it into K subarrays, such that the sum of the squares of the subarray sums is minimized.

Examples:

Input: arr []= {1, 3, 2, 6, 7, 4}, K = 3.
Output: 193
Explanation: The optimal division into subarrays is [1, 3, 2], [6] and [7, 4],
Giving a total sum of (1 + 3 + 2)2 + (6)2 + (7 + 4)2 = 193.
This is the minimum possible sum for this particular array and K.

Input: arr[] = {1, 4, 2, 3}, K = 2
Output: 50
Explanation: Divide it into subarrays {1, 4} and {2, 3}.
The sum is (1+4)2 + (2 + 3)2 = 52 + 52 = 50.
This is the minimum possible sum.

Suboptimal solution: The problem can be solved based on the following idea:

• If first j-1 elements are divided into i-1 groups then the minimum cost of dividing first j elements into i groups is the same as the minimum value among all possible combination of dividing first k-1 (i ≤ k ≤ j) elements into i-1 groups and the cost of the ith group formed by taking elements from kth to jth indices.
• Let dp[i][j] be the minimum sum obtainable by dividing the first j elements into i subarrays.
So the dp-transition will be –

dp[i][j] = mini≤k≤j (dp[i-1][k-1] + cost[k][i])

where cost[k][i] denotes the square of the sum of all elements in the subarray arr[k, k+1 . . . i]

Follow the steps mentioned below for solving the problem:

• The cost function can be calculated in constant time by preprocessing using a prefix sum array:
• Calculate prefix sum (say stored in pref[] array).
• So cost(i, j) can be calculated as (pref[j] – pref[i-1]).
• Traverse from i = 1 to M:
• Traverse from j = i to N:
• Traverse using k and form the dp[][] table using the above dp observation.
• The value at dp[M-1][N-1] is the required answer.

Below is the implementation of the above approach.

## C++

 `// C++ code to implement the approach` `#include ``using` `namespace` `std;` `// Function to find the minimum sum``int` `solve(``int` `arr[], ``int` `N, ``int` `M)``{``    ``int` `pref[N + 1], dp[M + 1][N + 1];` `    ``// Prefix sum array``    ``pref[0] = 0;``    ``for` `(``int` `i = 0; i < N; i++)``        ``pref[i + 1] = pref[i] + arr[i];` `    ``// Initialize the dp array``    ``for` `(``int` `i = 0; i < N; i++)``        ``dp[0][i] = pref[i + 1] * pref[i + 1];` `    ``// Fill in the dp array``    ``for` `(``int` `i = 1; i < M; i++) {``        ``for` `(``int` `j = i; j < N; j++) {``            ``dp[i][j] = INT_MAX;``            ``for` `(``int` `k = 1; k <= j; k++) {``                ``int` `cost``                    ``= (pref[j + 1] - pref[k])``                    ``* (pref[j + 1] - pref[k]);` `                ``// dp transition``                ``dp[i][j] = min(dp[i][j],``                               ``dp[i - 1][k - 1]``                               ``+ cost);``            ``}``        ``}``    ``}` `    ``return` `dp[M - 1][N - 1];``}` `// Driver code``int` `main()``{``    ``int` `N, M = 3;``    ``int` `arr[] = { 1, 3, 2, 6, 7, 4 };``    ``N = ``sizeof``(arr) / ``sizeof``(arr[0]);` `    ``// Function call``    ``cout << solve(arr, N, M);``    ``return` `0;``}`

## Java

 `// Java code to implement the approach``import` `java.io.*;` `class` `GFG {``    ``// Function to find the minimum sum``    ``public` `static` `int` `solve(``int` `arr[], ``int` `N, ``int` `M)``    ``{``        ``int` `pref[] = ``new` `int``[N + ``1``];``        ``int` `dp[][] = ``new` `int``[M + ``1``][N + ``1``];` `        ``// Prefix sum array``        ``pref[``0``] = ``0``;``        ``for` `(``int` `i = ``0``; i < N; i++)``            ``pref[i + ``1``] = pref[i] + arr[i];` `        ``// Initialize the dp array``        ``for` `(``int` `i = ``0``; i < N; i++)``            ``dp[``0``][i] = pref[i + ``1``] * pref[i + ``1``];` `        ``// Fill in the dp array``        ``for` `(``int` `i = ``1``; i < M; i++) {``            ``for` `(``int` `j = i; j < N; j++) {``                ``dp[i][j] = Integer.MAX_VALUE;``                ``for` `(``int` `k = ``1``; k <= j; k++) {``                    ``int` `cost = (pref[j + ``1``] - pref[k])``                               ``* (pref[j + ``1``] - pref[k]);` `                    ``// dp transition``                    ``dp[i][j] = Math.min(``                        ``dp[i][j], dp[i - ``1``][k - ``1``] + cost);``                ``}``            ``}``        ``}` `        ``return` `dp[M - ``1``][N - ``1``];``    ``}` `    ``// Driver Code``    ``public` `static` `void` `main(String[] args)``    ``{``        ``int` `N, M = ``3``;``        ``int` `arr[] = { ``1``, ``3``, ``2``, ``6``, ``7``, ``4` `};``        ``N = arr.length;` `        ``// Function call``        ``System.out.print(solve(arr, N, M));``    ``}``}` `// This code is contributed by Rohit Pradhan`

## Python3

 `import` `sys``# Function to find the minimum sum``def` `solve(arr, N, M) :``    ` `    ``pref ``=` `[``0``] ``*` `(N ``+` `1``)``    ``dp ``=` `[[``0``] ``*` `(N ``+` `1``) ] ``*` `(M``+``1``)` `    ``# Prefix sum array``    ``pref[``0``] ``=` `0``    ``for` `i ``in` `range``(N) :``        ``pref[i ``+` `1``] ``=` `pref[i] ``+` `arr[i]` `    ``# Initialize the dp array``    ``for` `i ``in` `range``(N) :``        ``dp[``0``][i] ``=` `pref[i ``+` `1``] ``*` `pref[i ``+` `1``]` `    ``# Fill in the dp array``    ``for` `i ``in` `range``(``1``, M) :``        ``for` `j ``in` `range``(i, N) :``            ``dp[i][j] ``=` `-``193``            ``for` `k ``in` `range``(``1``, j``+``1``) :``                ``cost ``=` `((pref[j ``+` `1``] ``-` `pref[k])``                    ``*` `(pref[j ``+` `1``] ``-` `pref[k]))` `                ``# dp transition``                ``dp[i][j] ``=` `min``(dp[i][j],``                               ``dp[i ``-` `1``][k ``-` `1``]``                               ``+` `cost);``            ` `    ``return` `(``-``dp[M ``-` `1``][N ``-` `1``])` `# Driver code``if` `__name__ ``=``=` `"__main__"``:``    ` `    ``N ``=` `3``    ``M ``=` `3``    ``arr ``=` `[ ``1``, ``3``, ``2``, ``6``, ``7``, ``4` `]``    ``N ``=` `len``(arr)` `    ``# Function call``    ``print``(solve(arr, N, M))` `    ``# This code is contributed by sanjoy_62.`

## C#

 `// C# program for the above approach``using` `System;``using` `System.Collections.Generic;` `class` `GFG``{` `    ``// Function to find the minimum sum``    ``static` `int` `solve(``int``[] arr, ``int` `N, ``int` `M)``    ``{``        ``int``[] pref = ``new` `int``[N + 1];``        ``int``[,] dp = ``new` `int``[M + 1, N + 1];` `        ``// Prefix sum array``        ``pref[0] = 0;``        ``for` `(``int` `i = 0; i < N; i++)``            ``pref[i + 1] = pref[i] + arr[i];` `        ``// Initialize the dp array``        ``for` `(``int` `i = 0; i < N; i++)``            ``dp[0, i] = pref[i + 1] * pref[i + 1];` `        ``// Fill in the dp array``        ``for` `(``int` `i = 1; i < M; i++) {``            ``for` `(``int` `j = i; j < N; j++) {``                ``dp[i, j] = Int32.MaxValue;``                ``for` `(``int` `k = 1; k <= j; k++) {``                    ``int` `cost = (pref[j + 1] - pref[k])``                               ``* (pref[j + 1] - pref[k]);` `                    ``// dp transition``                    ``dp[i, j] = Math.Min(``                        ``dp[i, j], dp[i - 1, k - 1] + cost);``                ``}``            ``}``        ``}` `        ``return` `dp[M - 1, N - 1];``    ``}` `// Driver Code``public` `static` `void` `Main(String[] args)``{``        ``int` `N, M = 3;``        ``int``[] arr = { 1, 3, 2, 6, 7, 4 };``        ``N = arr.Length;` `        ``// Function call``        ``Console.WriteLine(solve(arr, N, M));``}``}` `// This code is contributed by code_hunt.`

## Javascript

 `// Javascript code to implement the approach` `// Function to find the minimum sum``const solve = (arr, N, M) => {``    ``let pref = ``new` `Array(N + 1).fill(0);``    ``let dp = ``new` `Array(M + 1).fill(0).map(() => ``new` `Array(N + 1).fill(0));``    ` `    ``// Prefix sum array``    ``pref[0] = 0;``    ``for` `(let i = 0; i < N; i++) {``        ``pref[i + 1] = pref[i] + arr[i];``    ``}``    ``// Initialize the dp array``    ``for` `(let i = 0; i < N; i++) {``        ``dp[0][i] = pref[i + 1] * pref[i + 1];``    ``}``    ` `    ``// Fill in the dp array``    ``for` `(let i = 1; i < M; i++) {``        ``for` `(let j = i; j < N; j++) {``            ``dp[i][j] = -193;``            ``for` `(let k = 1; k < j + 1; k++) {``                ``let cost = (pref[j + 1] - pref[k]) * (pref[j + 1] - pref[k]);``                ` `                ``// dp transition``                ``dp[i][j] = Math.min(dp[i][j], dp[i - 1][k - 1] + cost);``            ``}``        ``}``    ``}` `    ``return` `-dp[M - 1][N - 1];``}` `// Driver Code``let N = 3;``let M = 3;``let arr = [1, 3, 2, 6, 7, 4];``N = arr.length;`` ``// Function call``console.log(solve(arr, N, M));` `// This code is contributed by ishankhandelwals.`

Output

`193`

Time Complexity: O(M * N2)
Auxiliary Space: O(M * N)

Optimal Solution (Using Divide and Conquer Optimization):

This problem follows the quadrangle We can, however, notice that the cost function satisfies the quadrangle inequality

cost(a, c) + cost(b, d) ≤ cost(a, d) + cost(b, c).

The following is the proof:

Let sum(p, q) denote the sum of values in range [p, q] (sub-array of arr[[]), and let x = sum(b, c), y = sum(a, c) − sum(b, c), and z = sum(b, d) − sum(b, c)

Using this notation, the quadrangle inequality becomes

(x + y)2 + (x + z)2 ≤ (x + y + z)2 + x2
which is equivalent to 0 ≤ 2yz.

Since y and z are nonnegative values, this completes the proof. We can thus use the divide and conquer optimization.

• There is one more layer of optimization in the space complexity that we can do. To calculate the dp[][] states for a certain value of j, we only need the values of the dp state for j-1
• Thus, maintaining 2 arrays of length N and swapping them after the dp[][] array has been filled for the current value of j removes a factor of K from the space complexity.

Note: This optimization can be used for all implementations of the divide and conquer DP speedup.

Follow the steps mentioned below to implement the idea:

• The cost function can be calculated using prefix sum as in the previous approach.
• Now for each fixed value of i (number of subarrays in which the array is divided):
• Traverse the whole array to find the minimum possible sum for i divisions.
• The value stored in dp[M%2][N-1] is the required answer.

Below is the implementation of the above approach.

## C++

 `// C++ code to implement the approach` `#include ``using` `namespace` `std;` `// Function to implement the``// divide and conquer optimization``void` `divide(``int` `l, ``int` `r, ``int` `optl, ``int` `optr,``            ``int` `i, vector> &dp,``            ``int` `pref[])``{``    ``if` `(l > r) ``        ``return``;` `    ``// Find middle value``    ``int` `mid = (l + r) >> 1;``  ` `    ``// Store the minimum cost and opt(i, j)``    ``pair<``int``, ``int``> best = {INT_MAX, -1};` `    ``// Find value of the best cost and opt(i, j)``    ``for` `(``int` `k = optl; k <= min(mid, optr);``         ``k++) {``        ``int` `cost = (pref[mid+1] - pref[k])``            ``* (pref[mid+1] - pref[k]);``        ``best = min(best,``                   ``{(k ? dp[(i+1)%2][k-1] : 0)``                    ``+ cost, k});``    ``}        ` `    ``// Store the minimum cost in the dp array``    ``dp[i][mid] = best.first;``    ``int` `opt = best.second;` `    ``// Recursively call the divide function``    ``// to fill the other dp states``    ``divide(l, mid - 1, optl, opt, i, dp, pref);``    ``divide(mid + 1, r, opt, optr, i, dp, pref);``}` `// Function to solve the problem``int` `solve(``int` `arr[], ``int` `N, ``int` `M)``{``    ``vector> dp(2, vector<``int``>(N));``    ` `    ``// Prefix sum array``    ``int` `pref[N + 1];``    ``pref[0] = 0;``    ``for` `(``int` `i = 0; i < N; i++)``        ``pref[i + 1] = pref[i] + arr[i];``    ` `      ``// Initialize the dp array``    ``for` `(``int` `i = 0; i < N; i++)``        ``dp[1][i] = pref[i + 1] * pref[i + 1];` `    ``// Fill in the dp array``    ``// with the divide function``    ``for` `(``int` `i = 2; i <= M; i++)``        ``divide(0, N - 1, 0, N - 1,``               ``(i%2), dp, pref);` `    ``return` `dp[M%2][N-1];``}` `// Driver code``int` `main()``{``    ``int` `N, M = 3;``    ``int` `arr[] = { 1, 3, 2, 6, 7, 4 };``    ``N = ``sizeof``(arr) / ``sizeof``(arr[0]);``  ` `    ``// Function call``    ``cout << solve(arr, N, M);``    ``return` `0;``}`

## Java

 `// Java code to implement the approach` `import` `java.io.*;``import` `java.util.*;` `// Pair class to store a pair of values``class` `Pair {``    ``int` `first;``    ``int` `second;``    ``public` `Pair(``int` `first, ``int` `second)``    ``{``        ``this``.first = first;``        ``this``.second = second;``    ``}``}` `class` `GFG {` `    ``// Function to implement the``    ``// divide and conquer optimization``    ``public` `static` `void` `divide(``int` `l, ``int` `r, ``int` `optl,``                              ``int` `optr, ``int` `i, ``int``[][] dp,``                              ``int``[] pref)``    ``{``        ``if` `(l > r)``            ``return``;` `        ``// Find middle value``        ``int` `mid = (l + r) >> ``1``;` `        ``// Store the minimum cost and opt(i, j)``        ``Pair best = ``new` `Pair(Integer.MAX_VALUE, -``1``);` `        ``// Find value of the best cost and opt(i, j)``        ``for` `(``int` `k = optl; k <= Math.min(mid, optr); k++) {``            ``int` `cost = (pref[mid + ``1``] - pref[k])``                       ``* (pref[mid + ``1``] - pref[k]);``            ``best = min(``                ``best,``                ``new` `Pair(``                    ``(k != ``0` `? dp[(i + ``1``) % ``2``][k - ``1``] : ``0``)``                        ``+ cost,``                    ``k));``        ``}` `        ``// Store the minimum cost in the dp array``        ``dp[i][mid] = best.first;``        ``int` `opt = best.second;` `        ``// Recursively call the divide function``        ``// to fill the other dp states``        ``divide(l, mid - ``1``, optl, opt, i, dp, pref);``        ``divide(mid + ``1``, r, opt, optr, i, dp, pref);``    ``}` `    ``// Function to solve the problem``    ``public` `static` `int` `solve(``int``[] arr, ``int` `N, ``int` `M)``    ``{``        ``int``[][] dp = ``new` `int``[``2``][N];` `        ``// Prefix sum array``        ``int``[] pref = ``new` `int``[N + ``1``];``        ``pref[``0``] = ``0``;``        ``for` `(``int` `i = ``0``; i < N; i++)``            ``pref[i + ``1``] = pref[i] + arr[i];` `        ``// Initialize the dp array``        ``for` `(``int` `i = ``0``; i < N; i++)``            ``dp[``1``][i] = pref[i + ``1``] * pref[i + ``1``];` `        ``// Fill in the dp array``        ``// with the divide function``        ``for` `(``int` `i = ``2``; i <= M; i++)``            ``divide(``0``, N - ``1``, ``0``, N - ``1``, (i % ``2``), dp, pref);` `        ``return` `dp[M % ``2``][N - ``1``];``    ``}` `    ``// Function to return the minimum of two pairs``    ``public` `static` `Pair min(Pair a, Pair b)``    ``{``        ``if` `(a.first < b.first) {``            ``return` `a;``        ``}``        ``return` `b;``    ``}` `    ``public` `static` `void` `main(String[] args)``    ``{``        ``int` `N, M = ``3``;``        ``int``[] arr = { ``1``, ``3``, ``2``, ``6``, ``7``, ``4` `};``        ``N = arr.length;` `        ``// Function call``        ``System.out.println(solve(arr, N, M));``    ``}``}` `// This code is contributed by lokesh.`

## Python3

 `# Python code to implement the approach``from` `typing ``import` `List``, ``Tuple` `# Function to implement the``# divide and conquer optimization``def` `divide(l: ``int``, r: ``int``, optl: ``int``, optr: ``int``,``            ``i: ``int``, dp: ``List``[``List``[``int``]],``            ``pref: ``List``[``int``]) ``-``> ``None``:``    ``if` `l > r: ``        ``return``  ` `    ``# Find middle value``    ``mid ``=` `(l ``+` `r) >> ``1``  ` `    ``# Store the minimum cost and opt(i, j)``    ``best ``=` `(``float``(``"inf"``), ``-``1``)` `    ``# Find value of the best cost and opt(i, j)``    ``for` `k ``in` `range``(optl, ``min``(mid, optr) ``+` `1``):``        ``cost ``=` `(pref[mid``+``1``] ``-` `pref[k]) ``*` `(pref[mid``+``1``] ``-` `pref[k])``        ``if` `(k ``and` `dp[(i``+``1``)``%``2``][k``-``1``]) ``+` `cost < best[``0``]:``            ``best ``=` `((k ``and` `dp[(i``+``1``)``%``2``][k``-``1``]) ``+` `cost, k)``  ` `    ``# Store the minimum cost in the dp array``    ``dp[i][mid] ``=` `best[``0``]``    ``opt ``=` `best[``1``]` `    ``# Recursively call the divide function``    ``# to fill the other dp states``    ``divide(l, mid ``-` `1``, optl, opt, i, dp, pref)``    ``divide(mid ``+` `1``, r, opt, optr, i, dp, pref)` `# Function to solve the problem``def` `solve(arr: ``List``[``int``], N: ``int``, M: ``int``) ``-``> ``int``:``    ``dp ``=` `[[``0``] ``*` `N ``for` `i ``in` `range``(``2``)]``    ` `    ``# Prefix sum array``    ``pref ``=` `[``0``] ``*` `(N ``+` `1``)``    ``pref[``0``] ``=` `0``    ``for` `i ``in` `range``(N):``        ``pref[i ``+` `1``] ``=` `pref[i] ``+` `arr[i]``    ` `    ``# Initialize the dp array``    ``for` `i ``in` `range``(N):``        ``dp[``1``][i] ``=` `pref[i ``+` `1``] ``*` `pref[i ``+` `1``]` `    ``# Fill in the dp array``    ``# with the divide function``    ``for` `i ``in` `range``(``2``, M``+``1``):``        ``divide(``0``, N ``-` `1``, ``0``, N ``-` `1``, (i``%``2``), dp, pref)` `    ``return` `dp[M``%``2``][N``-``1``]` `# Driver code``if` `__name__ ``=``=` `'__main__'``:``    ``N ``=` `6``    ``M ``=` `3``    ``arr ``=` `[``1``, ``3``, ``2``, ``6``, ``7``, ``4``]``  ` `    ``# Function call``    ``print``(solve(arr, N, M))``    ` `# This code is contributed by ik_9`

## C#

 `// C# code for the above approach``using` `System;` `// Pair class to store a pair of values``public` `class` `Pair {``  ``public` `int` `first;``  ``public` `int` `second;``  ``public` `Pair(``int` `first, ``int` `second)``  ``{``    ``this``.first = first;``    ``this``.second = second;``  ``}``}` `public` `class` `GFG {` `  ``// Function to implement the``  ``// divide and conquer optimization``  ``public` `static` `void` `divide(``int` `l, ``int` `r, ``int` `optl,``                            ``int` `optr, ``int` `i, ``int``[][] dp,``                            ``int``[] pref)``  ``{``    ``if` `(l > r)``      ``return``;` `    ``// Find middle value``    ``int` `mid = (l + r) >> 1;` `    ``// Store the minimum cost and opt(i, j)``    ``Pair best = ``new` `Pair(``int``.MaxValue, -1);` `    ``// Find value of the best cost and opt(i, j)``    ``for` `(``int` `k = optl; k <= Math.Min(mid, optr); k++) {``      ``int` `cost = (pref[mid + 1] - pref[k])``        ``* (pref[mid + 1] - pref[k]);``      ``best = min(``        ``best,``        ``new` `Pair(``          ``(k != 0 ? dp[(i + 1) % 2][k - 1] : 0)``          ``+ cost,``          ``k));``    ``}` `    ``// Store the minimum cost in the dp array``    ``dp[i][mid] = best.first;``    ``int` `opt = best.second;` `    ``// Recursively call the divide function``    ``// to fill the other dp states``    ``divide(l, mid - 1, optl, opt, i, dp, pref);``    ``divide(mid + 1, r, opt, optr, i, dp, pref);``  ``}` `  ``// Function to solve the problem``  ``public` `static` `int` `solve(``int``[] arr, ``int` `N, ``int` `M)``  ``{``    ``int``[][] dp = ``new` `int``[2][];``    ``for` `(``int` `i = 0; i < 2; i++)``      ``dp[i] = ``new` `int``[N];` `    ``// Prefix sum array``    ``int``[] pref = ``new` `int``[N + 1];``    ``pref[0] = 0;``    ``for` `(``int` `i = 0; i < N; i++)``      ``pref[i + 1] = pref[i] + arr[i];` `    ``// Initialize the dp array``    ``for` `(``int` `i = 0; i < N; i++)``      ``dp[1][i] = pref[i + 1] * pref[i + 1];` `    ``// Fill in the dp array``    ``// with the divide function``    ``for` `(``int` `i = 2; i <= M; i++)``      ``divide(0, N - 1, 0, N - 1, (i % 2), dp, pref);` `    ``return` `dp[M % 2][N - 1];``  ``}` `  ``// Function to return the minimum of two pairs``  ``public` `static` `Pair min(Pair a, Pair b)``  ``{``    ``if` `(a.first < b.first) {``      ``return` `a;``    ``}``    ``return` `b;``  ``}` `  ``static` `public` `void` `Main()``  ``{` `    ``// Code``    ``int` `N, M = 3;``    ``int``[] arr = { 1, 3, 2, 6, 7, 4 };``    ``N = arr.Length;` `    ``// Function call``    ``Console.WriteLine(solve(arr, N, M));``  ``}``}` `// This code is contributed by lokeshmvs21.`

## Javascript

 `// JavaScript code to implement the approach` `// Function to implement the``// divide and conquer optimization``function` `divide(l, r, optl, optr, i, dp, pref) {``if` `(l > r) ``return``;` `// Find middle value``let mid = (l + r) >> 1;` `// Store the minimum cost and opt(i, j)``let best = [Infinity, -1];` `// Find value of the best cost and opt(i, j)``for` `(let k = optl; k <= Math.min(mid, optr); k++) {``let cost = (pref[mid + 1] - pref[k]) * (pref[mid + 1] - pref[k]);``if` `((k && dp[(i + 1) % 2][k - 1]) + cost < best[0]) {``best = [(k && dp[(i + 1) % 2][k - 1]) + cost, k];``}``}` `// Store the minimum cost in the dp array``dp[i][mid] = best[0];``let opt = best[1];` `// Recursively call the divide function``// to fill the other dp states``divide(l, mid - 1, optl, opt, i, dp, pref);``divide(mid + 1, r, opt, optr, i, dp, pref);``}` `// Function to solve the problem``function` `solve(arr, N, M) {``let dp = Array(2);``for` `(let i = 0; i < 2; i++) dp[i] = Array(N).fill(0);` `// Prefix sum array``let pref = Array(N + 1).fill(0);``pref[0] = 0;``for` `(let i = 0; i < N; i++) {``pref[i + 1] = pref[i] + arr[i];``}` `// Initialize the dp array``for` `(let i = 0; i < N; i++) {``dp[1][i] = pref[i + 1] * pref[i + 1];``}` `// Fill in the dp array``// with the divide function``for` `(let i = 2; i <= M; i++) {``divide(0, N - 1, 0, N - 1, (i % 2), dp, pref);``}` `return` `dp[M % 2][N - 1];``}` `// Driver code``let N = 6;``let M = 3;``let arr = [1, 3, 2, 6, 7, 4];` `// Function call``document.write(solve(arr, N, M));`

Output

`193`

Time Complexity: O(M * N * logN)
Auxiliary Space: O(N)

My Personal Notes arrow_drop_up