题意:二维平面上给N个点,求一个面积最小的矩形,使得所有点都在矩形内或矩形边上。
链接:http://codeforces.com/gym/100633/problem/E
解法:显然先求凸包,在凸包上选择矩形。用旋(xuan1)转(zhuan4)卡(qia3)壳(ke2) 的方法,O(N)枚举两条不断旋转的平行线,对于每对平行线,计算出另两条矩形的平行边,使得矩形面积最小。难点在于如何计算另两条矩形的平行边,正解如下,将凸包的每条边当成一条直线,直线与x轴正方向可求一个a角,( 0≤a<π ),存储下来,排序。对于每对旋转的平行边,求与其垂直的直线的b角,在凸包每条直线中找到第一个不小于b角的直线,往左取10条,往右取10条,分别将取得的直线对应线段的端点存储下来,在存储下来的点中找到两个能“包括“凸包的点,接着求得对应的矩形,不断维护面积最小的矩形,输出即可。spj,任意满足条件矩形即可。
总结:此题想法容易,编码困难,适合反复咀嚼多练,以提高对旋转卡壳、计算几何中二分的熟练度,甚而能达到简化此类题目的方法目的更好。
//Hello. I'm Peter.
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const double eps = 1e-9, pi = acos(-1.0);
inline int sgn(double x){
if(fabs(x) < eps) return 0;
else return x > 0? 1 : -1;
}
struct Point{
double x, y;
Point(){};
Point(double x1, double y1){x = x1, y = y1;}
};
typedef Point Vector;
bool cmpxy(const Point a, const Point b){if(sgn(a.x-b.x)) return a.x < b.x; else return a.y < b.y;}
bool cmpyx(const Point a, const Point b){if(sgn(a.y-b.y)) return a.y < b.y; else return a.x < b.x;}
bool cmpYx(const Point a, const Point b){if(sgn(a.y-b.y)) return a.y > b.y; else return a.x < b.x;}
bool cmpyX(const Point a, const Point b){if(sgn(a.y-b.y)) return a.y < b.y; else return a.x > b.x;}
Vector operator + (const Vector a, const Vector b){return Vector(a.x + b.x, a.y + b.y);}
Vector operator - (const Vector a, const Vector b){return Vector(a.x - b.x, a.y - b.y);}
double operator * (const Vector a, const Vector b){return a.x * b.x + a.y * b.y;}
double operator % (const Vector a, const Vector b){return a.x * b.y - a.y * b.x;}
Vector operator * (const Vector a, const double b){return Vector(a.x * b, a.y * b);}
Vector operator * (const double b, const Vector a){return Vector(a.x * b, a.y * b);}
Vector operator / (const Vector a, const double b){return Vector(a.x / b, a.y / b);}
bool operator == (const Point a, const Point b){return sgn(a.x - b.x)==0 && sgn(a.y - b.y)==0;}
bool operator || (const Vector a, const Vector b){return sgn(a % b)==0;}
bool operator / (const Vector a, const Vector b){return sgn(a % b)!=0;}
double Length(Vector v){return (double)sqrt((double)(v.x * v.x + v.y * v.y));}
double Dis(Point a, Point b){return Length(a - b);}
Vector Rotate(Vector v, double rad){return Vector(v.x * cos(rad) - v.y * sin(rad), v.x * sin(rad) + v.y * cos(rad));}
Vector Norv(Vector v){return Vector(v.y, -v.x);}
Vector Unitv(Vector v){return v / Length(v);}
double angle(Vector v){return atan2(v.y, v.x);}
double angle(Vector a, Vector b){
double ans = angle(a) - angle(b);
while(sgn(ans) < 0) ans += 2*pi; while(sgn(ans) >= 2*pi) ans -= 2*pi;
return fmin(ans, 2*pi - ans);
}
double Area_Tri(Point p1, Point p2, Point p3){return 0.5 * fabs((p2 - p1) % (p3 - p1));}
double Area_Tri(double a, double b, double c){
double p = (a+b+c)/2;
return (double)sqrt((double)(p*(p-a)*(p-b)*(p-c)));
}
double Area_Polygon(Point *p, int n){
//求任意简单多边形的无向面积,前提要将p逆时针或顺时针排序
if(n <= 2) return 0;
Point o = Point(0, 0);
p[n++] = p[0];
double ans = 0;
for(int i = 0; i < n-1; i++){
ans += Area_Tri(o, p[i], p[i+1]) * (sgn((p[i] - o) % (p[i+1] - o)) < 0? -1 : 1);
}
return fabs(ans);
}
struct Line{
Point p; Vector v;
Line(){};
Line(Point p1, Vector v1){p = p1, v = v1;}
};
Point operator / (const Line a, const Line b){
double t = ((b.p - a.p) % b.v) / (a.v % b.v);
return a.p + a.v * t;
}
double Dis(Point p, Line l){return fabs(l.v % (p - l.p)) / Length(l.v);}
double angle(Line a, Line b){double ans = angle(a.v, b.v); return fmin(ans, pi - ans);}
double angle(Line l){
double a = angle(Vector(1, 0), l.v);
if(Rotate(Vector(1, 0), a) / l.v) a = pi - a;
return a;
}
struct Seg{
Point p1, p2;
Seg(){};
Seg(Point p11, Point p22){p1 = p11, p2 = p22;}
};
bool operator / (const Seg a, const Seg b){//need change
return sgn((a.p2 - a.p1) % (b.p1 - a.p1)) * sgn((a.p2 - a.p1) % (b.p2 - a.p1)) <= 0 &&
sgn((b.p2 - b.p1) % (a.p1 - b.p1)) * sgn((b.p2 - b.p1) % (a.p2 - b.p1)) <= 0 ;
}
bool operator / (const Line a, const Seg b){
return sgn(a.v % (b.p1 - a.p)) * sgn(a.v % (b.p2 - a.p)) <= 0;
}
bool PointOnSeg(Point p, Seg s){
if((s.p1 - p) / (s.p2 - p)) return false;
else if(sgn((s.p1 - p) * (s.p2 - p)) > 0) return false;
else return true;
}
int n, m;
#define N 80010
Point readPoi(){
int x, y;
x = read(), y = read();
return Point(x, y);
}
Seg seg[N];
struct Data{
int id;
double rad;
}data[N];
int numdata;
bool cmp1(const Data a, const Data b){
return a.rad < b.rad;
}
int binary_find(double rad){
int l = 1, r = numdata, mid;
while(l < r){
mid = (l + r) >> 1;
if(sgn(data[mid].rad - rad) >= 0) r = mid;
else l = mid + 1;
}
return l;
}
struct Answer{
Point p[10];
Point c[10];
double s;
}anstem,ans;
/*--------------------------以下是应用型算法-----------------------------------*/
void ConvexHull(Point *p, int n, Point *c, int &m){
sort(p, p + n, cmpxy);
m = 0;
for(int i = 0; i < n; i++){
while(m > 1 && sgn((c[m-1] - c[m-2]) % (p[i] - c[m-1])) <= 0) m--;
c[m++] = p[i];
}
int k = m;
for(int i = n - 2; i >= 0; i--){
while(m > k && sgn((c[m-1] - c[m-2]) % (p[i] - c[m-1])) <= 0) m--;
c[m++] = p[i];
}
if(m > 1) m--;
}
void Rotate_Convex_Hull(Point *c, int m){
int id1 = 0, id2 = 0;
for(int i = 0; i < m; i++) if(cmpYx(c[i], c[id1])) id1 = i;
for(int i = 0; i < m; i++) if(cmpyX(c[i], c[id2])) id2 = i;
int nexid1 = 0, nexid2 = 0;
Vector v1 = Vector(-1, 0), v2 = Vector(1, 0), nexv1, nexv2;
double rad1, rad2;
int rm1 = id1, rm2 = id2;
ans.s = 1e100;
while(1){
//solve here...
Vector norv = Norv(v1);
double rad = angle(Line(c[id1], norv));
int t1 = binary_find(rad);
Point temp[100]; int numtemp = 0;
int t = t1;
for(int i = 0; i < 10; i++){
int id = data[t].id;
temp[++numtemp] = seg[id].p1, temp[++numtemp] = seg[id].p2;
t = t == numdata? 1 : t+1;
}
t = t1;
for(int i = 0; i < 10; i++){
int id = data[t].id;
temp[++numtemp] = seg[id].p1, temp[++numtemp] = seg[id].p2;
t = t == 1? numdata : t-1;
}
Point p = c[id1] + v1 * 1e9, p1, p2;
double mini = 1e18;
for(int i = 1; i <= numtemp; i++){
double d1 = Dis(temp[i], Line(p, norv));
if(d1 < mini){
mini = d1;
p1 = temp[i];
}
}
p = c[id1] - v1 * 1e9;
mini = 1e18;
for(int i = 1; i <= numtemp; i++){
double d1 = Dis(temp[i], Line(p, norv));
if(d1 < mini){
mini = d1;
p2 = temp[i];
}
}
int nump = 0, mt;
anstem.p[nump++] = Line(c[id1], v1) / Line(p1, norv);
anstem.p[nump++] = Line(c[id2], v2) / Line(p1, norv);
anstem.p[nump++] = Line(c[id1], v1) / Line(p2, norv);
anstem.p[nump++] = Line(c[id2], v2) / Line(p2, norv);
ConvexHull(anstem.p, 4, anstem.c, mt);
anstem.s = Area_Polygon(anstem.c, mt);
if(anstem.s < ans.s) ans = anstem;
//solve done
nexid1 = id1 == m-1? 0 : id1+1;
nexid2 = id2 == m-1? 0 : id2+1;
nexv1 = c[nexid1] - c[id1];
nexv2 = c[nexid2] - c[id2];
rad1 = angle(v1, nexv1);
rad2 = angle(v2, nexv2);
if(sgn(rad1 - rad2) < 0){
v1 = nexv1;
id1 = nexid1;
v2 = Rotate(v2, rad1);
}
else if(sgn(rad2 - rad1) < 0){
v2 = nexv2;
id2 = nexid2;
v1 = Rotate(v1, rad2);
}
else{
v1 = nexv1;
id1 = nexid1;
v2 = nexv2;
id2 = nexid2;
}
if(id1 == rm1 && id2 == rm2) break;
}
}
Point p[N], c[N];
int main(){
//freopen("/Users/peteryuanpan/data.txt","r",stdin);
n = read();
for(int i = 0; i < n; i++) p[i] = readPoi();
ConvexHull(p, n, c, m);
// for(int i = 0; i < m; i++){
// printf("%.3f %.3f\n",c[i].x,c[i].y);
// }
numdata = 0;
for(int i = 0; i < m; i++){
int nex = i == m-1 ? 0 : i + 1;
seg[i].p1 = c[i], seg[i].p2 = c[nex];
++numdata;
data[numdata].id = i;
data[numdata].rad = angle(Line(c[i], c[nex] - c[i]));
}
sort(data + 1, data + 1 + numdata, cmp1);
Rotate_Convex_Hull(c, m);
for(int i = 0; i < 4; i++){
printf("%.10f %.10f\n",ans.c[i].x,ans.c[i].y);
}
return 0;
}