Counting Collinear Point Triplets in a Cartesian Plane
Given a collection of coordinates on a two-dimensional grid, the task requires enumerating every distinct triplet of vertices to determine how many lie on a single straight line. The brute-force methodology iterates through all valid index combinations, enforcing strictly increasing indices to eliminate duplicate evaluations and self-pairings. Geometric alignment can be verified through several mathematically equivalent approaches.
Gradient Comparison
Three vertices align when the slope between the first two matches the slope between the last two. Vertical arrangements cause division by zero if handled naively, so explicit checks for identical x-coordinates must precede any ratio calculation. Using floating-point division remains viable when vertical cases are isolated.
#include <vector>
#include <iostream>
using namespace std;
struct Coord { int h; int v; };
bool checkGradient(const Coord& a, const Coord& b, const Coord& c) {
int dh1 = b.h - a.h, dv1 = b.v - a.v;
int dh2 = c.h - b.h, dv2 = c.v - b.v;
if (dh1 == 0 && dh2 == 0) return true;
if (dh1 == 0 || dh2 == 0) return false;
return (double)dv1 / dh1 == (double)dv2 / dh2;
}
int main() {
int total;
cin >> total;
vector<Coord> grid(total);
for(auto &pt : grid) cin >> pt.h >> pt.v;
int matches = 0;
for(int i = 0; i < total; ++i)
for(int j = i + 1; j < total; ++j)
for(int k = j + 1; k < total; ++k)
if(checkGradient(grid[i], grid[j], grid[k])) ++matches;
cout << matches << endl;
return 0;
}
Vector Cross Product
By treating the points as directional segments sharing a common origin, alignment corresponds to vector parallelism. The two-dimensional cross product of vectors formed by the points yields zero precisely when they are collinear. This technique operates entirely with integer aritmhetic, avoiding precision drift.
#include <iostream>
#include <vector>
using namespace std;
int main() {
int pointCount;
if(!(cin >> pointCount)) return 0;
struct Vec { int x, y; };
vector<Vec> pts(pointCount);
for(int i = 0; i < pointCount; ++i) cin >> pts[i].x >> pts[i].y;
int aligned = 0;
for(int i = 0; i < pointCount; ++i) {
for(int j = i + 1; j < pointCount; ++j) {
for(int k = j + 1; k < pointCount; ++k) {
long long cross = (long long)(pts[j].x - pts[i].x) * (pts[k].y - pts[i].y) -
(long long)(pts[j].y - pts[i].y) * (pts[k].x - pts[i].x);
if(cross == 0) aligned++;
}
}
}
cout << aligned << "\n";
return 0;
}
Determinant Expansion
Placing the coordinates into a 3x3 matrix alongside a constant column of ones creates a system where linear dependence results in a zero determinant. Algebraically expanding this determinant produces a single linear combination of coordinate products. Evaluating this expression provides a direct integer-based test.
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<pair<int,int>> coords(n);
for(auto& p : coords) cin >> p.first >> p.second;
int tally = 0;
for(int a = 0; a < n; ++a) {
for(int b = a + 1; b < n; ++b) {
for(int c = b + 1; c < n; ++c) {
int xa = coords[a].first, ya = coords[a].second;
int xb = coords[b].first, yb = coords[b].second;
int xc = coords[c].first, yc = coords[c].second;
long long det = (long long)xa * yb + (long long)ya * xc + (long long)xb * yc -
(long long)xa * yc - (long long)yb * xc - (long long)xb * ya;
if(det == 0) tally++;
}
}
}
cout << tally << endl;
return 0;
}
Triangle Area Evaluation
Aligned points cannot enclose a region, meaning the geometric area of the triangle they form must be exactly zero. The shoelace formula component for this calculation reduces to checking whether the weighted sum of coordinate differences equals zero. Scaling factors are irrelevant when validating against zero.
#include <iostream>
#include <vector>
using namespace std;
struct Point { int x, y; };
bool hasZeroArea(const Point& p1, const Point& p2, const Point& p3) {
int val = p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y);
return val == 0;
}
int main() {
int limit;
cin >> limit;
vector<Point> data(limit);
for(auto& p : data) cin >> p.x >> p.y;
int result = 0;
for(int i = 0; i < limit; ++i)
for(int j = i + 1; j < limit; ++j)
for(int k = j + 1; k < limit; ++k)
if(hasZeroArea(data[i], data[j], data[k])) result++;
cout << result << endl;
return 0;
}
Each implementation maintains a cubic time complexity $O(n^3)$ due to the nested iteration over the input set. For standard problem constraints where the coordinate count remains relatively small, this computational overhead stays well within typical execution limits.