Open In App

Divide and Conquer Optimization in Dynamic Programming

Last Updated : 13 Apr, 2023
Summarize
Comments
Improve
Suggest changes
Like Article
Like
Save
Share
Report
News Follow

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)



Similar Reads

Comparison among Greedy, Divide and Conquer and Dynamic Programming algorithm
Greedy algorithm, divide and conquer algorithm, and dynamic programming algorithm are three common algorithmic paradigms used to solve problems. Here's a comparison among these algorithms: Approach:Greedy algorithm: Makes locally optimal choices at each step with the hope of finding a global optimum.Divide and conquer algorithm: Breaks down a probl
4 min read
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 main idea of dynamic programming (DP) and how it is d
12 min read
Difference between Greedy Algorithm and Divide and Conquer Algorithm
Greedy algorithm and divide and conquer algorithm are two common algorithmic paradigms used to solve problems. The main difference between them lies in their approach to solving problems. Greedy Algorithm:The greedy algorithm is an algorithmic paradigm that follows the problem-solving heuristic of making the locally optimal choice at each stage wit
3 min read
Search in a Row-wise and Column-wise Sorted 2D Array using Divide and Conquer algorithm
Given an n x n matrix, where every row and column is sorted in increasing order. Given a key, how to decide whether this key is in the matrix. A linear time complexity is discussed in the previous post. This problem can also be a very good example for divide and conquer algorithms. Following is divide and conquer algorithm.1) Find the middle elemen
15+ min read
Advantages and Disadvantages of Divide and Conquer Algorithms
Divide and Conquer is an algorithmic paradigm in which the problem is solved using the Divide, Conquer, and Combine strategy. A typical divide-and-conquer algorithm solves a problem using the following three steps: Divide: This involves dividing the problem into smaller sub-problems.Conquer: Solve sub-problems by calling recursively until solved.Co
2 min read
Closest Pair of Points using Divide and Conquer algorithm
We are given an array of n points in the plane, and the problem is to find out the closest pair of points in the array. This problem arises in a number of applications. For example, in air-traffic control, you may want to monitor planes that come too close together, since this may indicate a possible collision. Recall the following formula for dist
15+ min read
Advanced master theorem for divide and conquer recurrences
The Master Theorem is a tool used to solve recurrence relations that arise in the analysis of divide-and-conquer algorithms. The Master Theorem provides a systematic way of solving recurrence relations of the form: T(n) = aT(n/b) + f(n) where a, b, and f(n) are positive functions and n is the size of the problem. The Master Theorem provides conditi
5 min read
Merge K sorted arrays | Set 3 ( Using Divide and Conquer Approach )
Giving k sorted arrays, each of size N, the task is to merge them into a single sorted array. Examples: Input: arr[][] = {{5, 7, 15, 18}, {1, 8, 9, 17}, {1, 4, 7, 7}} Output: {1, 1, 4, 5, 7, 7, 7, 8, 9, 15, 17, 18} Input: arr[][] = {{3, 2, 1} {6, 5, 4}} Output: {1, 2, 3, 4, 5, 6} Prerequisite: Merge SortSimple Approach: A simple solution is to appe
15+ min read
Merge K sorted arrays of different sizes | ( Divide and Conquer Approach )
Given k sorted arrays of different length, merge them into a single array such that the merged array is also sorted.Examples: Input : {{3, 13}, {8, 10, 11} {9, 15}} Output : {3, 8, 9, 10, 11, 13, 15} Input : {{1, 5}, {2, 3, 4}} Output : {1, 2, 3, 4, 5} Let S be the total number of elements in all the arrays.Simple Approach: A simple approach is to
8 min read
Sum of maximum of all subarrays | Divide and Conquer
Given an array arr[] of length N, the task is to find the sum of the maximum elements of every possible sub-array of the array.Examples: Input : arr[] = {1, 3, 1, 7} Output : 42 Max of all sub-arrays: {1} - 1 {1, 3} - 3 {1, 3, 1} - 3 {1, 3, 1, 7} - 7 {3} - 3 {3, 1} - 3 {3, 1, 7} - 7 {1} - 1 {1, 7} - 7 {7} - 7 1 + 3 + 3 + 7 + 3 + 3 + 7 + 1 + 7 + 7 =
12 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 subproblems.Each subproblem is solved independent
2 min read
Maximum Subarray Sum using Divide and Conquer algorithm
You are given a one dimensional array that may contain both positive and negative integers, find the sum of contiguous subarray of numbers which has the largest sum. For example, if the given array is {-2, -5, 6, -2, -3, 1, 5, -6}, then the maximum subarray sum is 7 (see highlighted elements). Recommended: Please solve it on “PRACTICE ” first, befo
12 min read
Divide and Conquer | Set 5 (Strassen's Matrix Multiplication)
Given two square matrices A and B of size n x n each, find their multiplication matrix. Naive Method: Following is a simple way to multiply two matrices. C/C++ Code void multiply(int A[][N], int B[][N], int C[][N]) { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { C[i][j] = 0; for (int k = 0; k < N; k++) { C[i][j] += A[i][k]*B[
15+ min read
Tiling Problem using Divide and Conquer algorithm
Given a n by n board where n is of form 2k where k >= 1 (Basically n is a power of 2 with minimum value as 2). The board has one missing cell (of size 1 x 1). Fill the board using L shaped tiles. A L shaped tile is a 2 x 2 square with one cell of size 1x1 missing. Figure 1: An example inputThis problem can be solved using Divide and Conquer. Bel
15+ min read
Longest Common Prefix using Divide and Conquer Algorithm
Given a set of strings, find the longest common prefix. Examples: Input : {“geeksforgeeks”, “geeks”, “geek”, “geezer”} Output : "gee" Input : {"apple", "ape", "april"} Output : "ap" We have discussed word by word matching and character by character matching algorithms.In this algorithm, a divide and conquer approach is discussed. We first divide th
7 min read
Convex Hull using Divide and Conquer Algorithm
In computational geometry, a convex hull is the smallest convex polygon that contains a given set of points. It is a fundamental concept with applications in various fields such as computer graphics, robotics, and image processing. Importance of Convex Hull:Convex hulls are important in computational geometry for several reasons: Collision detectio
15 min read
Divide and Conquer in Python
Divide and Conquer is an effective approach for managing challenges that divides a major problem into smaller, easier-to-manage subproblems. The solution to the main problem is obtained by combining the final solutions from multiple individually solved subproblems. This approach works especially well with issues that naturally break down into diffe
3 min read
Karatsuba algorithm for fast multiplication using Divide and Conquer algorithm
Given two binary strings that represent value of two integers, find the product of two strings. For example, if the first bit string is "1100" and second bit string is "1010", output should be 120. For simplicity, let the length of two strings be same and be n. A Naive Approach is to follow the process we study in school. One by one take all bits o
15+ min read
Divide and Conquer Algorithm
Divide and Conquer algorithm is a problem-solving strategy that involves breaking down a complex problem into smaller, more manageable parts, solving each part individually, and then combining the solutions to solve the original problem. It is a widely used algorithmic technique in computer science and mathematics. Example: In the Merge Sort algori
4 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 first explore the optimization itself, and then s
15+ min read
Dynamic Programming in Game Theory for Competitive Programming
In the fast-paced world of competitive programming, mastering dynamic programming in game theory is the key to solving complex strategic challenges. This article explores how dynamic programming in game theory can enhance your problem-solving skills and strategic insights, giving you a competitive edge. Whether you're a seasoned coder or a newcomer
15+ min read
Decrease and Conquer
As divide-and-conquer approach is already discussed, which include following steps: Divide the problem into a number of subproblems that are smaller instances of the same problem. Conquer the sub problems by solving them recursively. If the subproblem sizes are small enough, however, just solve the sub problems in a straightforward manner. Combine
5 min read
Transform and Conquer Technique
The Transform and conquer technique is a way of solving problems by breaking them down into smaller subproblems, solving the smaller subproblems, and then combining the solutions to the subproblems to solve the original problem. This technique can be used to solve problems in many different areas, including mathematics, computer science, and engine
5 min read
Problem Reduction in Transform and Conquer Technique
What is Problem Reduction?Problem reduction is an algorithm design technique that takes a complex problem and reduces it to a simpler one. The simpler problem is then solved and the solution of the simpler problem is then transformed to the solution of the original problem. Problem reduction is a powerful technique that can be used to simplify comp
7 min read
Master Theorem For Subtract and Conquer Recurrences
Master theorem is used to determine the Big - O upper bound on functions which possess recurrence, i.e which can be broken into sub problems. Master Theorem For Subtract and Conquer Recurrences: Let T(n) be a function defined on positive n as shown below: for some constants c, a>0, b>0, k>=0 and function f(n). If f(n) is O(nk), then1. If a
3 min read
Representation Change in Transform and Conquer Technique
Representation Change is one of the variants of the Transfer and Conquer technique where the given problem is transformed into another domain that is more familiar or simpler to execute. In the case of representation change, the instance of a given problem is transformed into another representation without affecting the original instance. Character
8 min read
Instance Simplification Method in Transform and Conquer Technique
Instance simplification is one of the Transform and Conquer techniques. To understand Instance Simplification, first let us understand what is transform and conquer. Transform and Conquer is a technique whose main idea is to transfer the problem into some easier or similar versions using some procedure and then solve that easier or simpler versions
9 min read
Minimum moves to make M and N equal by repeated addition of divisors except 1 using Dynamic Programming
Given two integers N and M, the task is to calculate the minimum number of moves to change N to M, where In one move it is allowed to add any divisor of the current value of N to N itself except 1. Print "-1" if it is not possible. Example: Input: N = 4, M = 24Output: 5Explanation: The given value of N can be converted into M using the following st
8 min read
Introduction and Dynamic Programming solution to compute nCr%p
Given three numbers n, r and p, compute value of nCr mod p. Example: Input: n = 10, r = 2, p = 13 Output: 6 Explanation: 10C2 is 45 and 45 % 13 is 6.We strongly recommend that you click here and practice it, before moving on to the solution.METHOD 1: (Using Dynamic Programming) A Simple Solution is to first compute nCr, then compute nCr % p. This s
15+ min read
Bitmasking and Dynamic Programming | Travelling Salesman Problem
In this post, we will be using our knowledge of dynamic programming and Bitmasking technique to solve one of the famous NP-hard problem "Traveling Salesman Problem".Before solving the problem, we assume that the reader has the knowledge of DP and formation of DP transition relationBitmasking in DPTraveling Salesman problem To understand this concep
15+ min read
three90RightbarBannerImg