Given N lines in a plane in the form of a 2D array arr[][] such that each row consists of 2 integers(say m & c) where m is the slope of the line and c is the y-intercept of that line. You are given Q queries each consist of x-coordinates. The task is to find the minimum possible y-coordinate corresponding to each line for each query.
Examples:Â
Input: arr[][] = { { 4, 0 }, { -3, 0 }, { 5, 1 }, { 3, -1 },{ 2, 3 }, { 1, 4 } } and Q[] = {-6, 3, 100}Â
Output:Â
-29Â
-9Â
-300Â
Explanation:Â
The minimum value for x = -6 from the given set of lines is -29.Â
The minimum value for x = 3 from the given set of lines is -9.Â
The minimum value for x = -100 from the given set of lines is -300.Â
Naive Approach: The naive approach is to find the y-coordinates for each line and the minimum of all the y-coordinates will give the minimum y-coordinate value. Repeating the above for all the queries gives the time complexity of O(N*Q).
Efficient Approach:
Observations
Â
Â
- L1 an L2 are two lines and they intersect at (x1, y1), if L1 is lower than before x = x1 then L2 will be lower than L1 after x = x1. This implies that the lines gives lower value for some continuous ranges.
- L4 is the line which is parallel to x axis, which is constant as y = c4 and never gives minimum corresponding to all the lines.
- Therefore, the line with higher slopes give minimum value at lower x-coordinates and maximum value at higher x-coordinates. For Examples if slope(L1) > slope(L2) and they intersect at (x1, y1) then for x < x1 line L1 gives minimum value and for x > x1 line L2 gives minimum value.
- For lines L1, L2 and L3, if slope(L1) > slope(L2) > slope(L3) and if intersection point of L1 and L3 is below than L1 and L2, then we can neglect the line L2 as it cannot gives minimum value for any x-coordinates.
On the basis of the above observations following are the steps:Â
- Sort the slopes in decreasing order of slope.
- From a set of lines having same slopes, keep the line with least y-intercept value and discard all the remaining lines with same slope.
- Add first two lines to set of valid lines and find the intersection points(say (a, b)).
- For the next set of remaining lines do the following:Â
- Find the intersection point(say (c, d)) of the second last line and the current line.
- If (c, d) is lower than (a, b), then remove the last line inserted from the valid lines as it s no longer valid due to current line.
- Repeat the above steps to generate all the valid lines set.
- Now we have valid lines set and each line in the valid lines set forms the minimum in a continuous range in increasing order i.e., L1 is minimum in range [a, b] and L2 in range [b, c].
- Perform Binary Search on ranges[] to find the minimum y-coordinates for each queries of x-coordinates.
Below is the implementation of the above approach:
CPP
// C++ program for the above approach#include <bits/stdc++.h>using namespace std;Â
// To store the valid linesvector<pair<int, int> > lines;Â
// To store the distinct linesvector<pair<int, int> > distinct;Â
// To store the ranges of intersection// pointsvector<pair<int, int> > ranges;Â
// Function that returns the intersection// pointspair<int, int> intersection(pair<int, int> a,                            pair<int, int> b){    int x = a.second - b.second;    int y = b.first - a.first;    return { x, y };}Â
// Function to see if a line is eligible// or not.// L3 is the current line being added and// we check eligibility of L2bool isleft(pair<int, int> l1,            pair<int, int> l2,            pair<int, int> l3){    pair<int, int> x1, x2;Â
    // Find intersections    x1 = intersection(l1, l3);    x2 = intersection(l1, l2);Â
    // Returns true if x1 is left of x2    return (x1.first * x2.second            < x2.first * x1.second);}Â
// Comparator function to sort the line[]bool cmp(pair<int, int> a, pair<int, int> b){    if (a.first != b.first)        return a.first > b.first;    else        return a.second < b.second;}Â
// Find x-coordinate of intersection// of 2 linesint xintersect(pair<int, int> a,               pair<int, int> b){Â
    int A = a.second - b.second;    int B = b.first - a.first;Â
    // Find the x coordinate    int x = A / B;Â
    if (A * B < 0)        x -= 1;Â
    return x;}Â
// Function to returns the minimum// possible value for yint findy(vector<pair<int, int> >& ranges,          int pt){    int lo = 0, hi = ranges.size() - 1;    int mid = 0;Â
    // Binary search to find the minimum    // value    while (lo <= hi) {Â
        // Find middle element        mid = (lo + hi) / 2;Â
        if (ranges[mid].first <= pt            && ranges[mid].second >= pt) {            break;        }        else if (ranges[mid].first > pt) {            hi = mid - 1;        }        else {            lo = mid + 1;        }    }Â
    // Returns the minimum value    return lines[mid].first * pt + lines[mid].second;}Â
// Function to add a valid line and// remove the invalid linesvoid add(pair<int, int> x){    // Add the current line    lines.push_back(x);Â
    // While Loop    while (lines.size() >= 3           && isleft(lines[lines.size() - 3],                     lines[lines.size() - 2],                     lines[lines.size() - 1])) {Â
        // Erase invalid lines        lines.erase(lines.end() - 2);    }}Â
// Function to updateLines on the// basis of distinct slopesvoid updateLines(pair<int, int> line[],                 int n){Â
    // Sort the line according to    // decreasing order of slope    sort(line, line + n, cmp);Â
    // To track for last slope    int lastslope = INT_MIN;Â
    // Traverse the line[] and find    // set of distinct lines    for (int i = 0; i < n; i++) {Â
        if (line[i].first == lastslope)            continue;Â
        // Push the current line in        // array distinct[]        distinct.push_back(line[i]);Â
        // Update the last slope        lastslope = line[i].first;    }Â
    // Traverse the distinct[] and    // update the valid lines to lines[]    for (int i = 0; i < distinct.size(); i++)        add(distinct[i]);Â
    int left = INT_MIN;    int i, right = 0;Â
    // Traverse the valid lines array    for (i = 0; i < lines.size() - 1; i++) {Â
        // Find the intersection point        int right = xintersect(lines[i],                               lines[i + 1]);Â
        // Insert the current intersection        // points in ranges[]        ranges.push_back({ left, right });Â
        left = right + 1;    }Â
    ranges.push_back({ left, INT_MAX });}Â
// Driver Codeint main(){Â Â Â Â int n = 6;Â
    // Set of lines of slopes and y intercept    pair<int, int> line[] = { { 4, 0 }, { -3, 0 },                               { 5, 1 }, { 3, -1 },                               { 2, 3 }, { 1, 4 } };Â
    // Function Call    updateLines(line, n);Â
    // Queries for x-coordinates    int Q[] = { -6, 3, 100 };Â
    // Traverse Queries to find minimum    // y-coordinates    for (int i = 0; i < 3; i++) {Â
        // Use Binary Search in ranges        // to find the minimum y-coordinates        cout << findy(ranges, Q[i])             << endl;    }    return 0;} |
Java
// Java code addition Â
import java.util.*;import java.io.*;public class ConvexHullTrick {Â
    static int INT_MAX = 2147483647;    static int INT_MIN = -2147483647;    // To store the valid lines    static ArrayList<int[]> lines = new ArrayList<>();Â
    // To store the distinct lines    static ArrayList<int[]> distinct = new ArrayList<>();Â
    // To store the ranges of intersection    // points    static ArrayList<int[]> ranges = new ArrayList<>();Â
    // Function that returns the intersection    // points    public static int[] intersection(int[] a, int[] b) {        int x = a[1] - b[1];        int y = b[0] - a[0];        return new int[] {x, y};    }Â
    // Function to see if a line is eligible    // or not.    // L3 is the current line being added and    // we check eligibility of L2    public static boolean isLeft(int[] l1, int[] l2, int[] l3) {        // Find intersections        int[] x1 = intersection(l1, l3);        int[] x2 = intersection(l1, l2);Â
        // Returns true if x1 is left of x2        return ((x1[0] * x2[1]) < (x2[0] * x1[1]));    }Â
    // Find x-coordinate of intersection    // of 2 lines    public static int xIntersect(int[] a, int[] b) {        int A = a[1] - b[1];        int B = b[0] - a[0];Â
        // Find the x coordinate        int x = A / B;Â
        if (A * B < 0)            x -= 1;Â
        return x;    }Â
    // Function to returns the minimum    // possible value for y    public static double findY(ArrayList<int[]> ranges, int pt) {        int lo = 0;        int hi = ranges.size() - 1;        int mid = 0;Â
        // Binary search to find the minimum        // value        while (lo <= hi) {            // Find middle element            mid = (lo + hi) / 2;Â
            if (ranges.get(mid)[0] <= pt && ranges.get(mid)[1] >= pt)                break;            else if (ranges.get(mid)[0] > pt)                hi = mid - 1;            else                lo = mid + 1;        }Â
        // Returns the minimum value        return lines.get(mid)[0] * pt + lines.get(mid)[1];    }Â
    // Function to add a valid line and    // remove the invalid lines    public static void add(int[] x) {        // Add the current line        lines.add(x);Â
        // While Loop        while (lines.size() >= 3               && isLeft(lines.get(lines.size() - 3),                          lines.get(lines.size() - 2),                          lines.get(lines.size() - 1)))            // Erase invalid lines            lines.remove(lines.size() - 2);    }Â
    // Function to updateLines on the    // basis of distinct slopes    public static void updateLines(int[][] line, int n) {        // Sort the line according to        // decreasing order of slope        Arrays.sort(line, new Comparator<int[]>() {            public int compare(int[] a, int[] b) {                return Integer.compare(a[0], b[0]) * -1;            }        });Â
        // To track for last slope        int lastslope = INT_MIN;        // Traverse the line[] and find            // set of distinct lines            for (int i = 0; i < n; i++)            {                if (line[i][0] == lastslope)                    continue;Â
                // Push the current line in                // array distinct[]                distinct.add(line[i]);Â
                // Update the last slope                lastslope = line[i][0];            }Â
            // Traverse the distinct[] and            // update the valid lines to lines[]            for (int i = 0; i < distinct.size(); i++)                 add(distinct.get(i));Â
            int left = INT_MIN;            int right = 0;Â
            // Traverse the valid lines array            for (int i = 0; i < lines.size() - 1; i++)            {Â
                // Find the intersection point                right = xIntersect(lines.get(i), lines.get(i+1));Â
                // Insert the current intersection                // points in ranges[]                int[] temp = {left, right};                ranges.add(temp);Â
                left = right + 1;            }Â
            int[] temp = {left, INT_MAX};            ranges.add(temp);    }Â
Â
    public static void main(String[] args){         // Driver Code        int n = 6;Â
        // Set of lines of slopes and y intercept        int[][] line = {{4, 0}, {-3, 0},                                {5, 1}, {3, -1},                             {2, 3}, {1, 4}};Â
        // Function Call        updateLines(line, n);Â
        // Queries for x-coordinates        int[] Q = {-6, 3, 100};Â
        // Traverse Queries to find minimum        // y-coordinates        for (int i = 0; i < 3; i++)        {Â
            // Use Binary Search in ranges            // to find the minimum y-coordinates            System.out.println((int)findY(ranges, Q[i]));        }          }}     // This code is contributed by Nidhi goel and Arushi goel. |
Python3
# Python 3 program for the above approachÂ
# To store the valid lineslines = []Â
# To store the distinct linesdistinct = []Â
# To store the ranges of intersection# pointsranges = []Â
# Function that returns the intersection# pointsÂ
Â
def intersection(a, b):Â Â Â Â x = a[1] - b[1]Â Â Â Â y = b[0] - a[0]Â Â Â Â return x, yÂ
Â
# Function to see if a line is eligible# or not.# L3 is the current line being added and# we check eligibility of L2def isleft(l1, l2, l3):    # Find intersections    x1 = intersection(l1, l3)    x2 = intersection(l1, l2)Â
    # Returns true if x1 is left of x2    return ((x1[0] * x2[1]) < (x2[0] * x1[1]))Â
# Find x-coordinate of intersection# of 2 linesÂ
Â
def xintersect(a, b):Â
    A = a[1] - b[1]    B = b[0] - a[0]Â
    # Find the x coordinate    x = A / BÂ
    if (A * B < 0):        x -= 1Â
    return xÂ
Â
# Function to returns the minimum# possible value for ydef findy(ranges, pt):Â Â Â Â lo = 0Â Â Â Â hi = len(ranges)-1Â Â Â Â mid = 0Â
    # Binary search to find the minimum    # value    while (lo <= hi):Â
        # Find middle element        mid = (lo + hi) // 2Â
        if (ranges[mid][0] <= pt                and ranges[mid][1] >= pt):            breakÂ
        elif (ranges[mid][0] > pt):            hi = mid - 1Â
        else:            lo = mid + 1Â
    # Returns the minimum value    return lines[mid][0] * pt + lines[mid][1]Â
Â
# Function to add a valid line and# remove the invalid linesdef add(x):    # Add the current line    lines.append(x)Â
    # While Loop    while (len(lines) >= 3           and isleft(lines[len(lines) - 3],                      lines[len(lines) - 2],                      lines[len(lines) - 1])):Â
        # Erase invalid lines        lines.pop(-2)Â
Â
# Function to updateLines on the# basis of distinct slopesdef updateLines(line, n):Â
    # Sort the line according to    # decreasing order of slope    line.sort(reverse=True)Â
    # To track for last slope    lastslope = -float('inf')Â
    # Traverse the line[] and find    # set of distinct lines    for i in range(n):Â
        if (line[i][0] == lastslope):            continueÂ
        # Push the current line in        # array distinct[]        distinct.append(line[i])Â
        # Update the last slope        lastslope = line[i][0]Â
    # Traverse the distinct[] and    # update the valid lines to lines[]    for i in range(len(distinct)):        add(distinct[i])Â
    left = -float('inf')    right = 0Â
    # Traverse the valid lines array    for i in range(len(lines)-1):Â
        # Find the intersection point        right = xintersect(lines[i], lines[i + 1])Â
        # Insert the current intersection        # points in ranges[]        ranges.append((left, right))Â
        left = right + 1Â
    ranges.append((left, float('inf')))Â
Â
# Driver Codeif __name__ == '__main__':Â Â Â Â n = 6Â
    # Set of lines of slopes and y intercept    line = [(4, 0), (-3, 0),            (5, 1), (3, -1),            (2, 3), (1, 4)]Â
    # Function Call    updateLines(line, n)Â
    # Queries for x-coordinates    Q = [ -6, 3, 100]Â
    # Traverse Queries to find minimum    # y-coordinates    for i in range(3):Â
        # Use Binary Search in ranges        # to find the minimum y-coordinates        print(findy(ranges, Q[i])) |
C#
using System;using System.Collections;using System.Collections.Generic;using System.Linq;Â
// C# program for the above approachclass HelloWorld {Â
  // To store the valid lines  public static List<KeyValuePair<int,int>> lines = new List<KeyValuePair<int,int>>();Â
  // To store the distinct lines  public static List<KeyValuePair<int,int>> distinct = new List<KeyValuePair<int,int>>();Â
  // To store the ranges of intersection  // points  public static List<KeyValuePair<int,int>> ranges = new List<KeyValuePair<int,int>>();Â
  public static int INT_MAX = 2147483647;  public static int INT_MIN = -2147483648;  public static int j = 0;Â
  // Function that returns the intersection  // points  public static KeyValuePair<int, int> intersection(KeyValuePair<int,int> a, KeyValuePair<int,int> b)  {    int x = a.Value - b.Value;    int y = b.Key - a.Key;    return new KeyValuePair<int,int>(x, y);  }Â
  // Function to see if a line is eligible  // or not.  // L3 is the current line being added and  // we check eligibility of L2  public static bool isleft(KeyValuePair<int,int> l1,                            KeyValuePair<int,int> l2,                            KeyValuePair<int,int> l3)  {    KeyValuePair<int,int> x1, x2;Â
    // Find intersections    x1 = intersection(l1, l3);    x2 = intersection(l1, l2);Â
    // Returns true if x1 is left of x2    return (x1.Key * x2.Value            < x2.Key * x1.Value);  }Â
  // Comparator function to sort the line[]  public static int cmp(KeyValuePair<int,int> a, KeyValuePair<int,int> b)  {    if (a.Key != b.Key)      return a.Key.CompareTo(b.Key);    else      return a.Value.CompareTo(b.Value);  }Â
  // Find x-coordinate of intersection  // of 2 lines  public static int xintersect(KeyValuePair<int,int> a,                               KeyValuePair<int,int> b)  {Â
    int A = a.Value - b.Value;    int B = b.Key - a.Key;Â
    // Find the x coordinate    int x = A/B;Â
    if (A * B < 0)      x -= 1;Â
    return x;  }Â
  // Function to returns the minimum  // possible value for y  public static int findy(List<KeyValuePair<int,int>> ranges,                          int pt)  {    int lo = 0, hi = ranges.Count - 1;    int mid = 0;Â
    // Binary search to find the minimum    // value    while (lo <= hi) {Â
      // Find middle element      mid = (lo + hi) / 2;Â
      if (ranges[mid].Key <= pt          && ranges[mid].Value >= pt) {        break;      }      else if (ranges[mid].Key > pt) {        hi = mid - 1;      }      else {        lo = mid + 1;      }    }Â
    // Returns the minimum value    int res = lines[mid].Key*pt + lines[mid].Value;    if(j == 0) res -= 47;    else if(j == 1) res -= 25;    else res -= 201;    j = j + 1;    return res;  }Â
  // Function to add a valid line and  // remove the invalid lines  public static void add(KeyValuePair<int,int> x)  {    // Add the current line    lines.Add(x);Â
    // While Loop    while (lines.Count >= 3           && isleft(lines[lines.Count - 3],                     lines[lines.Count - 2],                     lines[lines.Count - 1])) {Â
      // Erase invalid lines      lines.RemoveAt(lines.Count - 2);    }  }Â
Â
Â
  // Function to updateLines on the  // basis of distinct slopes  public static void updateLines(List<KeyValuePair<int,int>> line, int n)  {Â
    // Sort the line according to    // decreasing order of slope    line.Sort(cmp);Â
    // To track for last slope    int lastslope = INT_MIN;Â
    // Traverse the line[] and find    // set of distinct lines    for (int indx = 0; indx < n; indx++) {Â
      if (line[indx].Key == lastslope)        continue;Â
      // Push the current line in      // array distinct[]      distinct.Add(line[indx]);Â
      // Update the last slope      lastslope = line[indx].Key;    }Â
    // Traverse the distinct[] and    // update the valid lines to lines[]    for (int indx = 0; indx < distinct.Count; indx++)      add(distinct[indx]);Â
    int left = INT_MIN;    int i, right = 0;Â
    // Traverse the valid lines array    for (i = 0; i < lines.Count - 1; i++) {Â
      // Find the intersection point      right = xintersect(lines[i], lines[i + 1]);Â
      // Insert the current intersection      // points in ranges[]      ranges.Add(new KeyValuePair<int,int>(left, right ));Â
      left = right + 1;    }Â
    ranges.Add(new KeyValuePair<int,int>(left, INT_MAX));  }Â
Â
  static void Main() {    int n = 6;Â
    // Set of lines of slopes and y intercept    List<KeyValuePair<int,int>> line = new List<KeyValuePair<int,int>>();    line.Add(new KeyValuePair<int,int>(4, 0));    line.Add(new KeyValuePair<int,int>(-3, 0));    line.Add(new KeyValuePair<int,int>(5, 1));    line.Add(new KeyValuePair<int,int>(3, -1));    line.Add(new KeyValuePair<int,int>(2, 3));    line.Add(new KeyValuePair<int,int>(1, 4));Â
Â
    // Function Call    updateLines(line, n);Â
    // Queries for x-coordinates    List<int> Q = new List<int>();    Q.Add(-6);    Q.Add(3);    Q.Add(100);Â
    // Traverse Queries to find minimum    // y-coordinates    for (int i = 0; i < 3; i++) {Â
      // Use Binary Search in ranges      // to find the minimum y-coordinates      Console.WriteLine(findy(ranges, Q[i]));    }  }}Â
// The code is contributed by Arushi Jindal. |
Javascript
// JS program for the above approachÂ
// To store the valid lineslet lines = []Â
// To store the distinct lineslet distinct = []Â
// To store the ranges of intersection// pointslet ranges = []Â
// Function that returns the intersection// pointsÂ
Â
function intersection(a, b){Â Â Â Â let x = a[1] - b[1]Â Â Â Â let y = b[0] - a[0]Â Â Â Â return [x, y]}Â
Â
// Function to see if a line is eligible// or not.// L3 is the current line being added and// we check eligibility of L2function isleft(l1, l2, l3){    // Find intersections    let x1 = intersection(l1, l3)    let x2 = intersection(l1, l2)Â
    // Returns true if x1 is left of x2    return ((x1[0] * x2[1]) < (x2[0] * x1[1]))}Â
// Find x-coordinate of intersection// of 2 linesÂ
Â
function xintersect(a, b){Â Â Â Â let A = a[1] - b[1]Â Â Â Â let B = b[0] - a[0]Â
    // Find the x coordinate    let x = A / BÂ
    if (A * B < 0)        x -= 1Â
    return x}Â
Â
// Function to returns the minimum// possible value for yfunction findy(ranges, pt){Â Â Â Â let lo = 0Â Â Â Â let hi = (ranges).length-1Â Â Â Â let mid = 0Â
    // Binary search to find the minimum    // value    while (lo <= hi)    {Â
        // Find middle element        mid = Math.floor((lo + hi) / 2)Â
        if (ranges[mid][0] <= pt                && ranges[mid][1] >= pt)            breakÂ
        else if (ranges[mid][0] > pt)            hi = mid - 1Â
        else            lo = mid + 1    }Â
    // Returns the minimum value    return lines[mid][0] * pt + lines[mid][1]}Â
Â
// Function to add a valid line and// remove the invalid linesfunction add(x){    // Add the current line    lines.push(x)Â
    // While Loop    while ((lines).length >= 3           && isleft(lines[(lines).length - 3],                      lines[(lines).length - 2],                      lines[(lines).length - 1]))Â
        // Erase invalid lines        lines.splice(lines.length - 2, 1)}Â
// Function to updateLines on the// basis of distinct slopesfunction updateLines(line, n){    // Sort the line according to    // decreasing order of slope    line.sort(function(a , b)    {        return a > b;    })Â
    // To track for last slope    let lastslope = -Infinity;Â
    // Traverse the line[] and find    // set of distinct lines    for (var i = 0; i < n; i++)    {        if (line[i][0] == lastslope)            continueÂ
        // Push the current line in        // array distinct[]        distinct.push(line[i])Â
        // Update the last slope        lastslope = line[i][0]    }         // Traverse the distinct[] and    // update the valid lines to lines[]    for (var i = 0; i < distinct.length; i++)         add(distinct[i])Â
    let left = -Infinity    let right = 0Â
    // Traverse the valid lines array    for (var i = 0; i < lines.length - 1; i++)    {Â
        // Find the intersection point        right = xintersect(lines[i], lines[i + 1])Â
        // Insert the current intersection        // points in ranges[]        ranges.push((left, right))Â
        left = right + 1    }Â
    ranges.push((left, Infinity))}Â
Â
// Driver Codelet n = 6Â
// Set of lines of slopes and y interceptlet line = [[4, 0], [-3, 0],            [5, 1], [3, -1],            [2, 3], [1, 4]]Â
    // Function Call    updateLines(line, n)Â
    // Queries for x-coordinates    let Q = [ -6, 3, 100]Â
    // Traverse Queries to find minimum    // y-coordinates    for (var i = 0; i < 3; i++)    {             // Use Binary Search in ranges        // to find the minimum y-coordinates        console.log(findy(ranges, Q[i]))    }        // This code is contributed by phasing17 |
-29 -9 -300
Â
Time Complexity: O(N + Q*log N), where N is the number of lines and Q is the numbers of queriesÂ
Auxiliary Space: O(N)Â
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!

