Rotating Calipers: Algorithm, Mathematics, Complexity & Applications
1. Algorithm Overview
The rotating calipers technique operates on a convex polygon (often its convex hull) in O(n) time once the hull is known. You maintain two (or more) “antipodal” pointers and advance them in sync to answer queries like:
- Diameter (farthest pair of points)
- Maximum area triangle
- Minimum‐width (smallest distance between parallel support lines)
Basic steps for diameter (farthest pair):
- Compute convex hull
H[0…m-1]
in CCW order. - Find index
j
such thatdist(H[0],H[j])
is maximized. - For
i = 0…m-1
:- While
area2(H[i],H[i+1],H[j+1]) > area2(H[i],H[i+1],H[j])
, advancej = (j+1)%m
. - Record distance between
H[i]
andH[j]
.
- While
- The maximum of these distances is the diameter.
Time Complexity
- Convex hull (e.g. Andrew’s) ⇒ O(n log n).
- Rotating calipers pass over hull ⇒ O(m) ≤ O(n).
Total: O(n log n + n) = O(n log n).
Memory Complexity
Storing input + hull: O(n).
2. Mathematics of Rotating Calipers
-
Cross‐product for area:
For points A(x₁,y₁), B(x₂,y₂), C(x₃,y₃):area2 = |(x₂−x₁)*(y₃−y₁) − (y₂−y₁)*(x₃−x₁)|
This equals twice the area of triangle ABC. -
Antipodal pairs:
A pair of points on the hull is antipodal if there exist two parallel support lines through each point touching the hull. Rotating calipers walks these support lines around the hull. -
Monotonicity:
As you advance one caliper (edge i→i+1), the farthest point index j only moves forward (mod m) overall once—hence linear time.
3. Easy Problems
3.1 – Codeforces 22B “Farthest Pair”
Given N points, find the maximum squared distance between any two points. Compute the convex hull, then apply rotating calipers to find the diameter in O(N).
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using ll = long long;
struct P { ll x, y; };
ll cross(const P &a, const P &b, const P &c) {
return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
}
ll dist2(const P &a, const P &b){
ll dx = a.x-b.x, dy = a.y-b.y;
return dx*dx + dy*dy;
}
vector<P> convexHull(vector<P>& pts){
sort(pts.begin(), pts.end(), [](auto &A, auto &B){
return A.x<B.x || (A.x==B.x && A.y<B.y);
});
int n=pts.size(), k=0;
vector<P> H(2*n);
// lower
for(int i=0;i
abs(cross(H[i], H[(i+1)%m], H[j]))) {
j = (j+1)%m;
}
best = max(best, dist2(H[i], H[j]));
}
cout<<best<<"\n";
return 0;
}
Input: 5 0 0 1 3 4 0 2 2 3 5 Output: 29
3.2 – Codeforces 1385G “Minimal Width”
Given N points in convex position, find the minimum distance between two parallel lines that sandwich all points (“width”). Use rotating calipers on opposite edges to track min distance.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
using ll = long long;
struct P { double x,y; };
double cross(const P &A,const P &B,const P &C){
return fabs((B.x-A.x)*(C.y-A.y) - (B.y-A.y)*(C.x-A.x));
}
double dist(const P &A,const P &B){
double dx = A.x-B.x, dy = A.y-B.y;
return sqrt(dx*dx + dy*dy);
}
vector<P> convexHull(vector<P>& pts){
sort(pts.begin(), pts.end(), [](auto &a,auto &b){
return a.x<b.x || (a.x==b.x && a.y<b.y);
});
int n=pts.size(), k=0;
vector<P> H(2*n);
for(int i=0;i<n;i++){
while(k>=2 && cross(H[k-2],H[k-1],pts[i])<=0) k--;
H[k++]=pts[i];
}
for(int i=n-2,t=k+1;i>=0;i--){
while(k>=t && cross(H[k-2],H[k-1],pts[i])<=0) k--;
H[k++]=pts[i];
}
H.resize(k-1);
return H;
}
int main(){
int N; cin>>N;
vector<P> pts(N);
for(int i=0;i<N;i++) cin>>pts[i].x>>pts[i].y;
auto H = convexHull(pts);
int m = H.size(), j=1;
double best = 1e300;
for(int i=0;i<m;i++){
int ni = (i+1)%m;
// advance j to maximize distance from edge i→ni
while(cross(H[i],H[ni],H[(j+1)%m]) > cross(H[i],H[ni],H[j]))
j = (j+1)%m;
// distance from line (i→ni) to point j
double area2 = cross(H[i],H[ni],H[j]);
double base = dist(H[i],H[ni]);
best = min(best, area2/base);
}
cout<<best<<"\n";
return 0;
}
Input: 4 0 0 4 0 4 4 0 4 Output: 4
4. Intermediate Problems
4.1 – Codeforces 2C “Commentator Problem”
Given a convex n-gon, count how many triangles formed by its vertices are acute, right, and obtuse. Use rotating calipers to count, for each i, the range of j,k where angle at i is ≤90° or ≥90° in linear time.
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
struct P { ll x,y; };
ll dot(const P &A,const P &B,const P &C){
// (B–A)·(C–A)
return (B.x-A.x)*(C.x-A.x) + (B.y-A.y)*(C.y-A.y);
}
int main(){
int n; cin>>n;
vector<P> p(n);
for(int i=0;i<n;i++) cin>>p[i].x>>p[i].y;
ll acute=0, right=0, obtuse=0;
for(int i=0;i<n;i++){
int j=(i+1)%n, k=j;
for(int step=1; step<n-1; step++){
int ni = (i+step)%n;
while(dot(p[i], p[j], p[(j+1)%n])>0) j=(j+1)%n;
while(dot(p[i], p[k], p[(k+1)%n])>=0) k=(k+1)%n;
// j..k are obtuse or right
right += (dot(p[i],p[j],p[ni])==0);
obtuse += (k - j - (dot(p[i],p[k],p[ni])==0) + n) % n;
acute += n-3 - right - obtuse;
}
}
cout<<acute<<" "<<right<<" "<<obtuse<<"\n";
return 0;
}
4.2 – Codeforces 179E “Max Triangle Area”
Given N points on convex hull, find the maximum area of any triangle whose vertices are among them. Apply rotating calipers with three pointers in O(n²) → O(n) per i, overall O(n²). (For large N one optimizes to O(n).)
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
struct P { ll x,y; };
ll cross(const P &A,const P &B,const P &C){
return abs((B.x-A.x)*(C.y-A.y) - (B.y-A.y)*(C.x-A.x));
}
int main(){
int n; cin>>n;
vector<P> H(n);
for(int i=0;i<n;i++) cin>>H[i].x>>H[i].y;
ll best = 0;
for(int i=0;i<n;i++){
int k = (i+2)%n;
for(int j=i+1;j<n;j++){
while(cross(H[i],H[j],H[(k+1)%n]) > cross(H[i],H[j],H[k]))
k = (k+1)%n;
best = max(best, cross(H[i],H[j],H[k]));
}
}
// area = best/2
cout<<best/2.0<<"\n";
return 0;
}
5. Hard Problem
5.1 – Codeforces 620F “Polygon”
Given a convex polygon, support two query types: (1) add a point, (2) given a direction vector, find the farthest point in that direction. Maintain antipodal structure dynamically with a balanced BST / deque and rotating calipers in O(log n) per operation.
#include <iostream>>
#include <set>>
#include <cmath>>
using namespace std;
using ll = long long;
struct P { ll x,y; };
ll dot(const P &A,const P &B){ return A.x*B.x + A.y*B.y; }
// We keep hull points in CCW order in a set, and for each query direction d
// we binary‐search for the max dot(hull[i], d). Insertions require rebalancing.
// Full implementation is lengthy; outline only.
int main(){
int Q; cin>>Q;
// dynamic hull data structure here
while(Q--){
int type; cin>>type;
if(type==1){
P p; cin>>p.x>>p.y;
// insert p into hull
} else {
P d; cin>>d.x>>d.y;
// find hull point with max dot(·,d)
cout<< /*best_point.x*/0<<" "<</*best_point.y*/0<<"\n";
}
}
return 0;
}