Skip to content
geeksforgeeks
  • Tutorials
    • Python
    • Java
    • Data Structures & Algorithms
    • ML & Data Science
    • Interview Corner
    • Programming Languages
    • Web Development
    • CS Subjects
    • DevOps And Linux
    • School Learning
    • Practice Coding Problems
  • Courses
    • DSA to Development
    • Get IBM Certification
    • Newly Launched!
      • Master Django Framework
      • Become AWS Certified
    • For Working Professionals
      • Interview 101: DSA & System Design
      • Data Science Training Program
      • JAVA Backend Development (Live)
      • DevOps Engineering (LIVE)
      • Data Structures & Algorithms in Python
    • For Students
      • Placement Preparation Course
      • Data Science (Live)
      • Data Structure & Algorithm-Self Paced (C++/JAVA)
      • Master Competitive Programming (Live)
      • Full Stack Development with React & Node JS (Live)
    • Full Stack Development
    • Data Science Program
    • All Courses
  • DSA
  • Interview Problems on DP
  • Practice DP
  • MCQs on DP
  • Tutorial on Dynamic Programming
  • Optimal Substructure
  • Overlapping Subproblem
  • Memoization
  • Tabulation
  • Tabulation vs Memoization
  • 0/1 Knapsack
  • Unbounded Knapsack
  • Subset Sum
  • LCS
  • LIS
  • Coin Change
  • Word Break
  • Egg Dropping Puzzle
  • Matrix Chain Multiplication
  • Palindrome Partitioning
  • DP on Arrays
  • DP with Bitmasking
  • Digit DP
  • DP on Trees
  • DP on Graph
Open In App
Next Article:
Divide and Conquer Optimization in Dynamic Programming
Next article icon

Divide and Conquer Optimization in Dynamic Programming

Last Updated : 13 Apr, 2023
Comments
Improve
Suggest changes
Like Article
Like
Report

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 <bits/stdc++.h> 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 <bits/stdc++.h> 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 <bits/stdc++.h> using namespace std;  // Function to implement the  // divide and conquer optimization void divide(int l, int r, int optl, int optr,              int i, vector<vector<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<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<vector<int>> 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)


Next Article
Divide and Conquer Optimization in Dynamic Programming

O

omkarbajaj000
Improve
Article Tags :
  • Dynamic Programming
  • Competitive Programming
  • DSA
Practice Tags :
  • Dynamic Programming

Similar Reads

    Dynamic Programming vs Divide-and-Conquer
    In this article I’m trying to explain the difference/similarities between dynamic programming and divide and conquer approaches based on two examples: binary search and minimum edit distance (Levenshtein distance).The ProblemWhen I started to learn algorithms it was hard for me to understand the mai
    12 min read
    Knuth's Optimization in Dynamic Programming
    Knuth's optimization is a very powerful tool in dynamic programming, that can be used to reduce the time complexity of the solutions primarily from O(N3) to O(N2). Normally, it is used for problems that can be solved using range DP, assuming certain conditions are satisfied. In this article, we will
    15+ min read
    Dynamic Programming meaning in DSA
    Dynamic Programming is defined as an algorithmic technique that is used to solve problems by breaking them into smaller subproblems and avoiding repeated calculation of overlapping subproblems and using the property that the solution of the problem depends on the optimal solution of the subproblems
    2 min read
    Dynamic Programming (DP) Introduction
    Dynamic Programming is a commonly used algorithmic technique used to optimize recursive solutions when same subproblems are called again.The core idea behind DP is to store solutions to subproblems so that each is solved only once. To solve DP problems, we first write a recursive solution in a way t
    15+ min read
    Divide and Conquer definition & meaning in DSA
    Divide and Conquer is a type of algorithm which involves breaking down a difficult problem into smaller subproblems, solving the subproblems individually and then merging the solutions of those subproblems to solve the actual problem. Properties of Divide and Conquer:Divides the problem into smaller
    2 min read
geeksforgeeks-footer-logo
Corporate & Communications Address:
A-143, 7th Floor, Sovereign Corporate Tower, Sector- 136, Noida, Uttar Pradesh (201305)
Registered Address:
K 061, Tower K, Gulshan Vivante Apartment, Sector 137, Noida, Gautam Buddh Nagar, Uttar Pradesh, 201305
GFG App on Play Store GFG App on App Store
Advertise with us
  • Company
  • About Us
  • Legal
  • Privacy Policy
  • In Media
  • Contact Us
  • Advertise with us
  • GFG Corporate Solution
  • Placement Training Program
  • Languages
  • Python
  • Java
  • C++
  • PHP
  • GoLang
  • SQL
  • R Language
  • Android Tutorial
  • Tutorials Archive
  • DSA
  • Data Structures
  • Algorithms
  • DSA for Beginners
  • Basic DSA Problems
  • DSA Roadmap
  • Top 100 DSA Interview Problems
  • DSA Roadmap by Sandeep Jain
  • All Cheat Sheets
  • Data Science & ML
  • Data Science With Python
  • Data Science For Beginner
  • Machine Learning
  • ML Maths
  • Data Visualisation
  • Pandas
  • NumPy
  • NLP
  • Deep Learning
  • Web Technologies
  • HTML
  • CSS
  • JavaScript
  • TypeScript
  • ReactJS
  • NextJS
  • Bootstrap
  • Web Design
  • Python Tutorial
  • Python Programming Examples
  • Python Projects
  • Python Tkinter
  • Python Web Scraping
  • OpenCV Tutorial
  • Python Interview Question
  • Django
  • Computer Science
  • Operating Systems
  • Computer Network
  • Database Management System
  • Software Engineering
  • Digital Logic Design
  • Engineering Maths
  • Software Development
  • Software Testing
  • DevOps
  • Git
  • Linux
  • AWS
  • Docker
  • Kubernetes
  • Azure
  • GCP
  • DevOps Roadmap
  • System Design
  • High Level Design
  • Low Level Design
  • UML Diagrams
  • Interview Guide
  • Design Patterns
  • OOAD
  • System Design Bootcamp
  • Interview Questions
  • Inteview Preparation
  • Competitive Programming
  • Top DS or Algo for CP
  • Company-Wise Recruitment Process
  • Company-Wise Preparation
  • Aptitude Preparation
  • Puzzles
  • School Subjects
  • Mathematics
  • Physics
  • Chemistry
  • Biology
  • Social Science
  • English Grammar
  • Commerce
  • World GK
  • GeeksforGeeks Videos
  • DSA
  • Python
  • Java
  • C++
  • Web Development
  • Data Science
  • CS Subjects
@GeeksforGeeks, Sanchhaya Education Private Limited, All rights reserved
We use cookies to ensure you have the best browsing experience on our website. By using our site, you acknowledge that you have read and understood our Cookie Policy & Privacy Policy
Lightbox
Improvement
Suggest Changes
Help us improve. Share your suggestions to enhance the article. Contribute your expertise and make a difference in the GeeksforGeeks portal.
geeksforgeeks-suggest-icon
Create Improvement
Enhance the article with your expertise. Contribute to the GeeksforGeeks community and help create better learning resources for all.
geeksforgeeks-improvement-icon
Suggest Changes
min 4 words, max Words Limit:1000

Thank You!

Your suggestions are valuable to us.

What kind of Experience do you want to share?

Interview Experiences
Admission Experiences
Career Journeys
Work Experiences
Campus Experiences
Competitive Exam Experiences