Most Collinear Points
National Olympiad — Regional round (2018 season)
Submission name: lines.cpp
Problem summary
Find the largest number of distinct given points lying on one straight line.
Input
n, followed by n integer coordinate pairs.
Output
The maximum collinear count.
Constraints
2 ≤ n ≤ 999; −99 ≤ x,y ≤ 99; all points are distinct.
Worked example
An independently prepared example for this explanation.
Input
6 0 0 2 1 -2 -1 -2 -2 6 3 -2 3
Output
4
Solution analysis
- Fix one input point as an anchor. Every other point defines an integer direction (dx,dy). Divide both components by gcd(|dx|,|dy|) to obtain a primitive direction.
- A line extends both ways, so normalize the sign: dx must be positive, or when dx=0, dy must be positive. Sort normalized directions; each run of equal directions is one line through the anchor. Add one for the anchor itself.
- In the example, (0,0), (2,1), (−2,−1) and (6,3) all reduce to direction (2,1) when anchored at the origin. The run has three other points, giving four in total.
Why it works
Two nonzero integer displacement vectors have the same normalized primitive direction exactly when they are parallel, allowing opposite signs. Therefore each sorted run consists precisely of the other points on one line through the anchor. Every optimal line contains an input anchor, so maximizing over anchors finds its complete size.
Watch out. Normalize opposite directions together. Avoid floating-point slopes, including special vertical-line cases. Distinctness ensures the gcd is never zero.
Commented C++17 solution
An independently written complete program with standard headers.
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<numeric>
#include<utility>
#include<vector>
// Algorithm: Fix one input point as an anchor.
using namespace std;
int main(){
int n;
cin>>n;
vector<pair<int,int>>points(n);
for(auto&point:points){
cin>>point.first>>point.second;
}
int answer=1;
for(int i=0;i<n;i++){
vector<pair<int,int>>direction;
for(int j=0;j<n;j++){
if(i==j){
continue;
}
int dx=points[j].first-points[i].first;
int dy=points[j].second-points[i].second;
int divisor=gcd(abs(dx),abs(dy));
dx/=divisor;
dy/=divisor;
if(dx<0||(dx==0&&dy<0)){
dx=-dx;
dy=-dy;
}
direction.emplace_back(dx,dy);
}
sort(direction.begin(),direction.end());
int run=0;
for(int j=0;j<static_cast<int>(direction.size());j++){
run=j>0&&direction[j]==direction[j-1]?run+1:1;
answer=max(answer,run+1);
}
}
cout<<answer<<'\n';
}