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-ordinates. 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 correspoding 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 neglact 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-cordinates.
Below is the implementation of the above approach:
// C++ program for the above approach #include <bits/stdc++.h> using namespace std; // To store the valid lines vector<pair< int , int > > lines; // To store the distinct lines vector<pair< int , int > > distinct; // To store the ranges of intersection // points vector<pair< int , int > > ranges; // Function that returns the intersection // points pair< 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 L2 bool 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 lines int 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 y int 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 lines void 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 slopes void 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 Code int 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; } |
-29 -9 -300
Time Complexity: O(N + Q*log N), where N is the number of lines and Q is the numbers of queries.