Minimum number of Straight Lines to connect all the given Points

Last Updated : 7 Feb, 2026

Given 2d integer array arr[][] containing N coordinates each of type {X, Y}, we have to find the minimum number of straight lines required to connect all the points of the array.
Note: after drawing those lines you can travel from any point to any other point by moving along the drawn lines.

Examples:

Input: arr[][] = {{1, 7}, {2, 6}, {3, 5}, {4, 4}, {5, 4}, {6, 3}, {7, 2}, {8, 1}}
Output: 3
Explanation: The diagram represents the input points and the minimum straight lines required.
The following 3 lines can be drawn to represent the line chart:
=> Line 1 (in red) which connects (1, 7), (2, 6), (3,5) and (4,4).
=> Line 2 (in blue) which connects from (4, 4) to (5, 4).
=> Line 3 (in green) which connects (5, 4), (6, 3), (7, 2), and (8, 1).

Minimum lines to connect the points of the example
Minimum lines to connect the points of the example

Input: arr[][] = {{3, 4}, {1, 2}, {7, 8}, {2, 3}}
Output: 1
Explanation: A single line passing through all the points is enough to connect them all.

Approach

Line Precomputation: The algorithm picks every pair of points (i, j) to define a line and checks every other point (k) for collinearity using cross-multiplication. Each line is stored as a bitmask where the set bits indicate the points it covers.

Bitmask DP: A DP table of size 2^N is used to store the minimum lines for every possible subset of points. We start with 0 points covered (dp[0] = 0).

Optimal Coverage: For each state, we identify the first point not yet covered and "try" every precomputed line that could cover it. By updating the DP table with the minimum value, we ensure that we find the most efficient combination of lines.

Below is an implementation for the above approach:

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>

using namespace std;

int minimumLines(vector<vector<int>>& points) {
    int n = points.size();
    if (n <= 2) return (n == 0) ? 0 : 1;

    // 1. Precompute all possible lines that can be formed
    // Each integer in this vector is a bitmask representing points on that line
    vector<int> lines;
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            int mask = (1 << i) | (1 << j);
            long long x1 = points[i][0], y1 = points[i][1];
            long long x2 = points[j][0], y2 = points[j][1];
            
            // Check which other points 'k' lie on the line formed by 'i' and 'j'
            for (int k = 0; k < n; k++) {
                if (k == i || k == j) continue;
                long long x3 = points[k][0], y3 = points[k][1];
                // Cross multiplication to check collinearity: (y2-y1)(x3-x2) == (y3-y2)(x2-x1)
                if ((y2 - y1) * (x3 - x2) == (y3 - y2) * (x2 - x1)) {
                    mask |= (1 << k);
                }
            }
            lines.push_back(mask);
        }
    }

    // 2. Bitmask DP to find the minimum lines to cover all points
    // dp[mask] = minimum lines to cover points represented by 'mask'
    vector<int> dp(1 << n, n); // Initialize with max possible lines (n)
    dp[0] = 0;

    for (int mask = 0; mask < (1 << n); mask++) {
        if (dp[mask] == n) continue; // Unreachable state
        
        // Find the first point not yet covered
        int first_uncovered = 0;
        while (first_uncovered < n && (mask & (1 << first_uncovered))) {
            first_uncovered++;
        }
        
        if (first_uncovered == n) break; // All points covered

        // Try covering the 'first_uncovered' point with every possible line it belongs to
        // Or treat it as a single point (though in this problem, any point can be a line)
        for (int line_mask : lines) {
            if (line_mask & (1 << first_uncovered)) {
                int next_mask = mask | line_mask;
                dp[next_mask] = min(dp[next_mask], dp[mask] + 1);
            }
        }
        
        // Case for a single point that doesn't form a line with others
        int solo_mask = mask | (1 << first_uncovered);
        dp[solo_mask] = min(dp[solo_mask], dp[mask] + 1);
    }

    return dp[(1 << n) - 1];
}

int main() {
    vector<vector<int>> vect{ {1, 0}, {2, 0}, {0, 0}, {4, 4}, {5, 5} };

    cout << "Minimum lines required: " << minimumLines(vect) << endl; 
    // Output: 2
    
    return 0;
}
Java
import java.util.*;

public class GFG {
    public static int minimumLines(int[][] points) {
        int n = points.length;
        if (n <= 2) return (n == 0)? 0 : 1;

        // 1. Precompute all possible lines that can be formed
        // Each integer in this vector is a bitmask representing points on that line
        List<Integer> lines = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int mask = (1 << i) | (1 << j);
                long x1 = points[i][0], y1 = points[i][1];
                long x2 = points[j][0], y2 = points[j][1];

                // Check which other points 'k' lie on the line formed by 'i' and 'j'
                for (int k = 0; k < n; k++) {
                    if (k == i || k == j) continue;
                    long x3 = points[k][0], y3 = points[k][1];
                    // Cross multiplication to check collinearity: (y2-y1)(x3-x2) == (y3-y2)(x2-x1)
                    if ((y2 - y1) * (x3 - x2) == (y3 - y2) * (x2 - x1)) {
                        mask |= (1 << k);
                    }
                }
                lines.add(mask);
            }
        }

        // 2. Bitmask DP to find the minimum lines to cover all points
        // dp[mask] = minimum lines to cover points represented by'mask'
        int[] dp = new int[1 << n];
        Arrays.fill(dp, n); // Initialize with max possible lines (n)
        dp[0] = 0;

        for (int mask = 0; mask < (1 << n); mask++) {
            if (dp[mask] == n) continue; // Unreachable state

            // Find the first point not yet covered
            int first_uncovered = 0;
            while (first_uncovered < n && ((mask & (1 << first_uncovered))!= 0)) {
                first_uncovered++;
            }

            if (first_uncovered == n) break; // All points covered

            // Try covering the 'first_uncovered' point with every possible line it belongs to
            // Or treat it as a single point (though in this problem, any point can be a line)
            for (int line_mask : lines) {
                if ((line_mask & (1 << first_uncovered))!= 0) {
                    int next_mask = mask | line_mask;
                    dp[next_mask] = Math.min(dp[next_mask], dp[mask] + 1);
                }
            }

            // Case for a single point that doesn't form a line with others
            int solo_mask = mask | (1 << first_uncovered);
            dp[solo_mask] = Math.min(dp[solo_mask], dp[mask] + 1);
        }

        return dp[(1 << n) - 1];
    }

    public static void main(String[] args) {
        int[][] vect = { {1, 0}, {2, 0}, {0, 0}, {4, 4}, {5, 5} };

        System.out.println("Minimum lines required: " + minimumLines(vect));
        // Output: 2
    }
}
Python
import itertools


def minimumLines(points):
    n = len(points)
    if n <= 2: return (n == 0) and 0 or 1

    # 1. Precompute all possible lines that can be formed
    # Each integer in this list is a bitmask representing points on that line
    lines = []
    for i, j in itertools.combinations(range(n), 2):
        mask = (1 << i) | (1 << j)
        x1, y1 = points[i]
        x2, y2 = points[j]

        # Check which other points 'k' lie on the line formed by 'i' and 'j'
        for k in range(n):
            if k == i or k == j: continue
            x3, y3 = points[k]
            # Cross multiplication to check collinearity: (y2-y1)*(x3-x2) == (y3-y2)*(x2-x1)
            if (y2 - y1) * (x3 - x2) == (y3 - y2) * (x2 - x1):
                mask |= (1 << k)
        lines.append(mask)

    # 2. Bitmask DP to find the minimum lines to cover all points
    # dp[mask] = minimum lines to cover points represented by 'mask'
    dp = [n] * (1 << n)  # Initialize with max possible lines (n)
    dp[0] = 0

    for mask in range(1 << n):
        if dp[mask] == n: continue  # Unreachable state

        # Find the first point not yet covered
        first_uncovered = 0
        while first_uncovered < n and (mask & (1 << first_uncovered)):
            first_uncovered += 1

        if first_uncovered == n: break  # All points covered

        # Try covering the 'first_uncovered' point with every possible line it belongs to
        # Or treat it as a single point (though in this problem, any point can be a line)
        for line_mask in lines:
            if line_mask & (1 << first_uncovered):
                next_mask = mask | line_mask
                dp[next_mask] = min(dp[next_mask], dp[mask] + 1)

        # Case for a single point that doesn't form a line with others
        solo_mask = mask | (1 << first_uncovered)
        dp[solo_mask] = min(dp[solo_mask], dp[mask] + 1)

    return dp[(1 << n) - 1]


# Example usage
vect = [[1, 0], [2, 0], [0, 0], [4, 4], [5, 5]]

print("Minimum lines required:", minimumLines(vect))
# Output: 2
C#
using System;
using System.Collections.Generic;
using System.Linq;

public class GFG
{
    public static void Main()
    {
        var vect = new List<Tuple<int, int>>
        {
            new Tuple<int, int>(1, 0),
            new Tuple<int, int>(2, 0),
            new Tuple<int, int>(0, 0),
            new Tuple<int, int>(4, 4),
            new Tuple<int, int>(5, 5)
        };

        Console.WriteLine("Minimum lines required: " + minimumLines(vect));
    }

    static int minimumLines(List<Tuple<int, int>> points)
    {
        int n = points.Count;
        if (n <= 2) return (n == 0)? 0 : 1;

        // 1. Precompute all possible lines that can be formed
        // Each integer in this list is a bitmask representing points on that line
        List<int> lines = new List<int>();
        for (int i = 0; i < n; i++)
        {
            for (int j = i + 1; j < n; j++)
            {
                int mask = (1 << i) | (1 << j);
                var p1 = points[i];
                var p2 = points[j];

                // Check which other points 'k' lie on the line formed by 'i' and 'j'
                for (int k = 0; k < n; k++)
                {
                    if (k == i || k == j) continue;
                    var p3 = points[k];
                    // Cross multiplication to check collinearity: (y2-y1)*(x3-x2) == (y3-y2)*(x2-x1)
                    if ((p2.Item2 - p1.Item2) * (p3.Item1 - p2.Item1) == (p3.Item2 - p2.Item2) * (p2.Item1 - p1.Item1))
                    {
                        mask |= (1 << k);
                    }
                }
                lines.Add(mask);
            }
        }

        // 2. Bitmask DP to find the minimum lines to cover all points
        int[] dp = Enumerable.Repeat(n, 1 << n).ToArray();  // Initialize with max possible lines (n)
        dp[0] = 0;

        for (int mask = 0; mask < (1 << n); mask++)
        {
            if (dp[mask] == n) continue;  // Unreachable state

            // Find the first point not yet covered
            int first_uncovered = 0;
            while (first_uncovered < n && (mask & (1 << first_uncovered))!= 0)
            {
                first_uncovered++;
            }

            if (first_uncovered == n) break;  // All points covered

            // Try covering the 'first_uncovered' point with every possible line it belongs to
            // Or treat it as a single point (though in this problem, any point can be a line)
            foreach (var line_mask in lines)
            {
                if ((line_mask & (1 << first_uncovered))!= 0)
                {
                    int next_mask = mask | line_mask;
                    dp[next_mask] = Math.Min(dp[next_mask], dp[mask] + 1);
                }
            }

            // Case for a single point that doesn't form a line with others
            int solo_mask = mask | (1 << first_uncovered);
            dp[solo_mask] = Math.Min(dp[solo_mask], dp[mask] + 1);
        }

        return dp[(1 << n) - 1];
    }
}
JavaScript
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

function Tuple(item1, item2) {
    this.Item1 = item1;
    this.Item2 = item2;
}

function minimumLines(points) {
    const n = points.length;
    if (n <= 2) return (n === 0)? 0 : 1;

    // 1. Precompute all possible lines that can be formed
    // Each integer in this list is a bitmask representing points on that line
    let lines = [];
    for (let i = 0; i < n; i++) {
        for (let j = i + 1; j < n; j++) {
            let mask = (1 << i) | (1 << j);
            let p1 = points[i];
            let p2 = points[j];

            // Check which other points 'k' lie on the line formed by 'i' and 'j'
            for (let k = 0; k < n; k++) {
                if (k === i || k === j) continue;
                let p3 = points[k];
                // Cross multiplication to check collinearity: (y2-y1)*(x3-x2) == (y3-y2)*(x2-x1)
                if ((p2.Item2 - p1.Item2) * (p3.Item1 - p2.Item1) === (p3.Item2 - p2.Item2) * (p2.Item1 - p1.Item1)) {
                    mask |= (1 << k);
                }
            }
            lines.push(mask);
        }
    }

    // 2. Bitmask DP to find the minimum lines to cover all points
    let dp = Array(1 << n).fill(n);  // Initialize with max possible lines (n)
    dp[0] = 0;

    for (let mask = 0; mask < (1 << n); mask++) {
        if (dp[mask] === n) continue;  // Unreachable state

        // Find the first point not yet covered
        let first_uncovered = 0;
        while (first_uncovered < n && (mask & (1 << first_uncovered))!== 0) {
            first_uncovered++;
        }

        if (first_uncovered === n) break;  // All points covered

        // Try covering the 'first_uncovered' point with every possible line it belongs to
        // Or treat it as a single point (though in this problem, any point can be a line)
        for (let line_mask of lines) {
            if ((line_mask & (1 << first_uncovered))!== 0) {
                let next_mask = mask | line_mask;
                dp[next_mask] = Math.min(dp[next_mask], dp[mask] + 1);
            }
        }

        // Case for a single point that doesn't form a line with others
        let solo_mask = mask | (1 << first_uncovered);
        dp[solo_mask] = Math.min(dp[solo_mask], dp[mask] + 1);
    }

    return dp[(1 << n) - 1];
}

let vect = [
    new Tuple(1, 0),
    new Tuple(2, 0),
    new Tuple(0, 0),
    new Tuple(4, 4),
    new Tuple(5, 5)
];

console.log("Minimum lines required: " + minimumLines(vect));

Output
Minimum lines required: 2

Time complexity: O(2^N x N^2)
Auxiliary Space: O(2^N)

Comment