Open In App

Find the maximum value of Y for a given X from given set of lines

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

Given a set of lines represented by a 2-dimensional array arr consisting of slope(m) and intercept(c) respectively and Q queries such that each query contains a value x. The task is to find the maximum value of y for each value of x from all the given a set of lines.

The given lines are represented by the equation y = m*x + c.

Examples:

Input: arr[][2] ={ {1, 1}, {0, 0}, {-3, 3} }, Q = {-2, 2, 1} Output: 9, 3, 2 For query x = -2, y values from the equations are -1, 0, 9. So the maximum value is 9 Similarly, for x = 2, y values are 3, 0, -3. So the maximum value is 3 And for x = 1, values of y = 2, 0, 0. So the maximum value is 2. Input: arr[][] ={ {5, 6}, {3, 2}, {7, 3} }, Q = { 1, 2, 30 } Output: 10, 17, 213

Naive Approach: The naive approach is to substitute the values of x in every line and compute the maximum of all the lines. For each query, it will take O(N) time and so the complexity of the solution becomes O(Q * N) where N is the number of lines and Q is the number of queries. Efficient approach: The idea is to use convex hull trick:

  • From the given set of lines, the lines which carry no significance (for any value of x they never give the maximal value y) can be found and deleted thereby reducing the set.
  • Now, if the ranges (l, r) can be found where each line gives the maximum value, then each query can be answered using binary search.
  • Therefore, a sorted vector of lines, with decreasing order of slopes, is created and the lines are inserted in decreasing order of the slopes.

Below is the implementation of the above approach: 

CPP




// C++ implementation of
// the above approach
 
#include <bits/stdc++.h>
using namespace std;
 
struct Line {
    int m, c;
 
public:
    // Sort the line in decreasing
    // order of their slopes
    bool operator<(Line l)
    {
 
        // If slopes aren't equal
        if (m != l.m)
            return m > l.m;
 
        // If the slopes are equal
        else
            return c > l.c;
    }
 
    // Checks if line L3 or L1 is better than L2
    // Intersection of Line 1 and
    // Line 2 has x-coordinate (b1-b2)/(m2-m1)
    // Similarly for Line 1 and
    // Line 3 has x-coordinate (b1-b3)/(m3-m1)
    // Cross multiplication will
    // give the below result
    bool check(Line L1, Line L2, Line L3)
    {
        return (L3.c - L1.c) * (L1.m - L2.m)
            < (L2.c - L1.c) * (L1.m - L3.m);
    }
};
 
struct Convex_HULL_Trick {
 
    // To store the lines
    vector<Line> l;
 
    // Add the line to the set of lines
    void add(Line newLine)
    {
 
        int n = l.size();
 
        // To check if after adding the new line
        // whether old lines are
        // losing significance or not
        while (n >= 2
            && newLine.check(l[n - 2],
                                l[n - 1],
                                newLine)) {
            n--;
        }
 
        l.resize(n);
 
        // Add the present line
        l.push_back(newLine);
    }
 
    // Function to return the y coordinate
    // of the specified line
    // for the given coordinate
    int value(int in, int x)
    {
        return l[in].m * x + l[in].c;
    }
 
    // Function to Return the maximum value
    // of y for the given x coordinate
    int maxQuery(int x)
    {
        // if there is no lines
        if (l.empty())
            return INT_MAX;
 
        int low = 0,
            high = (int)l.size() - 2;
 
        // Binary search
        while (low <= high) {
            int mid = (low + high) / 2;
 
            if (value(mid, x)
                < value(mid + 1, x))
                low = mid + 1;
            else
                high = mid - 1;
        }
 
        return value(low, x);
    }
};
 
// Driver code
int main()
{
    Line lines[] = { { 1, 1 },
                    { 0, 0 },
                    { -3, 3 } };
    int Q[] = { -2, 2, 1 };
    int n = 3, q = 3;
    Convex_HULL_Trick cht;
 
    // Sort the lines
    sort(lines, lines + n);
 
    // Add the lines
    for (int i = 0; i < n; i++)
        cht.add(lines[i]);
 
    // For each query in Q
    for (int i = 0; i < q; i++) {
        int x = Q[i];
        cout << cht.maxQuery(x) << endl;
    }
 
    return 0;
}


Python3




# Python3 implementation of the above approach
class Line: 
    def __init__(self, a = 0, b = 0):
        self.m = a;
        self.c = b;
      
    # Sort the line in decreasing
    # order of their slopes
    def __gt__(self, l):
      
        # If slopes arent equal
        if (self.m != l.m):
            return self.m > l.m;
 
        # If the slopes are equal
        else:
            return self.c > l.c;
      
    # Checks if line L3 or L1 is better than L2
    # Intersection of Line 1 and
    # Line 2 has x-coordinate (b1-b2)/(m2-m1)
    # Similarly for Line 1 and
    # Line 3 has x-coordinate (b1-b3)/(m3-m1)
    # Cross multiplication will give the below result
    def check(self, L1, L2, L3):
      
        return (L3.c - L1.c) * (L1.m - L2.m)  < (L2.c - L1.c) * (L1.m - L3.m);
      
class Convex_HULL_Trick  :
 
    # To store the lines
    def __init__(self):
      
        self.l = [];
      
    # Add the line to the set of lines
    def add(self, newLine):
        n = len(self.l)
 
        # To check if after adding the new line
        # whether old lines are
        # losing significance or not
        while (n >= 2 and newLine.check((self.l)[n - 2], (self.l)[n - 1], newLine)):
            n -= 1;
          
        # Add the present line
        (self.l).append(newLine);
      
    # Function to return the y coordinate
    # of the specified line for the given coordinate
    def value(self, ind, x):
        return (self.l)[ind].m * x + (self.l)[ind].c;
      
      
    # Function to Return the maximum value
    # of y for the given x coordinate
    def maxQuery(self, x):
     
        # if there is no lines
        if (len(self.l) == 0):
            return 999999999;
 
        low = 0
        high = len(self.l) - 2;
 
        # Binary search
        while (low <= high):
            mid = int((low + high) / 2);
 
            if (self.value(mid, x) < self.value(mid + 1, x)):
                low = mid + 1;
            else:
                high = mid - 1;
         
        return self.value(low, x);
     
# Driver code
lines = [ Line(1, 1), Line(0, 0), Line(-3, 3)]
 
Q = [ -2, 2, 1];
n = 3
q = 3;
cht = Convex_HULL_Trick();
 
# Sort the lines
lines.sort(reverse = True)
 
# Add the lines
for i in range(n):
    cht.add(lines[i]);
 
# For each query in Q
for i in range(q):
    x = Q[i];
    print(cht.maxQuery(x));
  
# This code is contributed by phasing17.


C#




// C# implementation of
// the above approach
 
using System;
using System.Collections.Generic;
 
class Line : IComparable<Line> {
  public int m, c;
 
  public Line(int m1, int c1)
  {
    m = m1;
    c = c1;
  }
 
  // Sort the line in decreasing
  // order of their slopes
  public int CompareTo(Line l)
  {
 
    // If slopes aren't equal
    if (m != l.m)
      return -m + l.m;
 
    // If the slopes are equal
    else
      return -c + l.c;
  }
 
  // Checks if line L3 or L1 is better than L2
  // Intersection of Line 1 and
  // Line 2 has x-coordinate (b1-b2)/(m2-m1)
  // Similarly for Line 1 and
  // Line 3 has x-coordinate (b1-b3)/(m3-m1)
  // Cross multiplication will
  // give the below result
  public bool check(Line L1, Line L2, Line L3)
  {
    return (L3.c - L1.c) * (L1.m - L2.m)
      < (L2.c - L1.c) * (L1.m - L3.m);
  }
};
 
class Convex_HULL_Trick {
 
  // To store the lines
  List<Line> l;
 
  public Convex_HULL_Trick() { l = new List<Line>(); }
 
  // Add the line to the set of lines
  public void add(Line newLine)
  {
 
    int n = l.Count;
 
    // To check if after adding the new line
    // whether old lines are
    // losing significance or not
    while (
      n >= 2
      && newLine.check(l[n - 2], l[n - 1], newLine)) {
      n--;
    }
 
    // Add the present line
    l.Add(newLine);
  }
 
  // Function to return the y coordinate
  // of the specified line
  // for the given coordinate
  public int value(int ind, int x)
  {
    return l[ind].m * x + l[ind].c;
  }
 
  // Function to Return the maximum value
  // of y for the given x coordinate
  public int maxQuery(int x)
  {
    // if there is no lines
    if (l.Count == 0)
      return Int32.MaxValue;
 
    int low = 0, high = (int)l.Count - 2;
 
    // Binary search
    while (low <= high) {
      int mid = (low + high) / 2;
 
      if (value(mid, x) < value(mid + 1, x))
        low = mid + 1;
      else
        high = mid - 1;
    }
 
    return value(low, x);
  }
};
 
class GFG {
 
  public static void Main(string[] args)
  {
    Line[] lines = { new Line(1, 1), new Line(0, 0),
                    new Line(-3, 3) };
    int[] Q = { -2, 2, 1 };
    int n = 3, q = 3;
    Convex_HULL_Trick cht = new Convex_HULL_Trick();
 
    // Sort the lines
    Array.Sort(lines);
 
    // Add the lines
    for (int i = 0; i < n; i++)
      cht.add(lines[i]);
 
    // For each query in Q
    for (int i = 0; i < q; i++) {
      int x = Q[i];
      Console.WriteLine(cht.maxQuery(x));
    }
  }
}
 
// This code is contributed by phasing17


Javascript




// JS implementation of
// the above approach
 
class Line {
    constructor(a = 0, b = 0 )
    {
        this.m = a
        this.c = b
    }
 
 
 
    // Sort the line in decreasing
    // order of their slopes
     compare (l)
    {
 
        // If slopes aren't equal
        if (this.m != l.m)
            return this.m > l.m;
 
        // If the slopes are equal
        else
            return this.c > l.c;
    }
 
    // Checks if line L3 or L1 is better than L2
    // Intersection of Line 1 and
    // Line 2 has x-coordinate (b1-b2)/(m2-m1)
    // Similarly for Line 1 and
    // Line 3 has x-coordinate (b1-b3)/(m3-m1)
    // Cross multiplication will
    // give the below result
     check(L1, L2, L3)
    {
        return (L3.c - L1.c) * (L1.m - L2.m)
            < (L2.c - L1.c) * (L1.m - L3.m);
    }
};
 
class Convex_HULL_Trick {
 
    // To store the lines
    constructor()
    {
        this.l = new Array();  
    }
 
    // Add the line to the set of lines
     add( newLine)
    {
 
        let n = (this.l).length;
 
        // To check if after adding the new line
        // whether old lines are
        // losing significance or not
        while (n >= 2
            && newLine.check((this.l)[n - 2],
                                (this.l)[n - 1],
                                newLine)) {
            n--;
        }
         
        while ((this.l).length > n)
            (this.l).pop();
         
         while ((this.l).length < n)
            (this.l).push(new Line());
         
 
        // Add the present line
        (this.l).push(newLine);
    }
 
    // Function to return the y coordinate
    // of the specified line
    // for the given coordinate
     value(ind, x)
    {
        return (this.l)[ind].m * x + (this.l)[ind].c;
    }
 
    // Function to Return the maximum value
    // of y for the given x coordinate
     maxQuery( x)
    {
        // if there is no lines
        if ((this.l).length == 0)
            return 999999999;
 
        let low = 0,
            high = (this.l).length - 2;
 
        // Binary search
        while (low <= high) {
            let mid = Math.floor((low + high) / 2);
 
            if (this.value(mid, x)     < this.value(mid + 1, x))
                low = mid + 1;
            else
                high = mid - 1;
        }
 
        return this.value(low, x);
    }
};
 
// Driver code
let lines = [ new Line(1, 1), new Line(0, 0), new Line(-3, 3)]
                 
let Q = [ -2, 2, 1 ];
let n = 3, q = 3;
let cht = new Convex_HULL_Trick();
 
    // Sort the lines
    lines.sort(function(a, b)
    {
        if (a.m == b.m)
            return a.c < b.c;
       return a.m < b.c;
    })
     
 
    // Add the lines
    for (var i = 0; i < n; i++)
        cht.add(lines[i]);
 
    // For each query in Q
    for (var i = 0; i < q; i++) {
        var x = Q[i];
        console.log(cht.maxQuery(x))
    }
 
// This code is contributed by phasing17


Java




// Java implementation of
// the above approach
 
import java.util.*;
 
 
class GFG {
 
static class Line implements Comparable<Line> {
  public int m, c;
 
  public Line(int m1, int c1)
  {
    m = m1;
    c = c1;
  }
 
  // Sort the line in decreasing
  // order of their slopes
  public int compareTo(Line l)
  {
 
    // If slopes aren't equal
    if (m != l.m)
      return -m + l.m;
 
    // If the slopes are equal
    else
      return -c + l.c;
  }
 
  // Checks if line L3 or L1 is better than L2
  // Intersection of Line 1 and
  // Line 2 has x-coordinate (b1-b2)/(m2-m1)
  // Similarly for Line 1 and
  // Line 3 has x-coordinate (b1-b3)/(m3-m1)
  // Cross multiplication will
  // give the below result
  public boolean check(Line L1, Line L2, Line L3)
  {
    return (L3.c - L1.c) * (L1.m - L2.m)
      < (L2.c - L1.c) * (L1.m - L3.m);
  }
};
 
static class Convex_HULL_Trick {
 
  // To store the lines
  ArrayList<Line> l;
 
  public Convex_HULL_Trick() { l = new ArrayList<Line>(); }
 
  // Add the line to the set of lines
  public void add(Line newLine)
  {
 
    int n = l.size();
 
    // To check if after adding the new line
    // whether old lines are
    // losing significance or not
    while (
      n >= 2
      && newLine.check(l.get(n - 2), l.get(n - 1), newLine)) {
      n--;
    }
 
    // Add the present line
    l.add(newLine);
  }
 
  // Function to return the y coordinate
  // of the specified line
  // for the given coordinate
  public int value(int ind, int x)
  {
    return (l.get(ind)).m * x + (l.get(ind)).c;
  }
 
  // Function to Return the maximum value
  // of y for the given x coordinate
  public int maxQuery(int x)
  {
    // if there is no lines
    if (l.size() == 0)
      return Integer.MAX_VALUE;
 
    int low = 0, high = (int)l.size() - 2;
 
    // Binary search
    while (low <= high) {
      int mid = (low + high) / 2;
 
      if (value(mid, x) < value(mid + 1, x))
        low = mid + 1;
      else
        high = mid - 1;
    }
 
    return value(low, x);
  }
};
 
  public static void main(String[] args)
  {
    Line[] lines = { new Line(1, 1), new Line(0, 0),
                    new Line(-3, 3) };
    int[] Q = { -2, 2, 1 };
    int n = 3, q = 3;
    Convex_HULL_Trick cht = new Convex_HULL_Trick();
 
    // Sort the lines
    Arrays.sort(lines);
 
    // Add the lines
    for (int i = 0; i < n; i++)
      cht.add(lines[i]);
 
    // For each query in Q
    for (int i = 0; i < q; i++) {
      int x = Q[i];
      System.out.println(cht.maxQuery(x));
    }
  }
}
 
// This code is contributed by phasing17


Output:

9
3
2


Last Updated : 03 Oct, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads