https://vjudge.net/problem/UVA-12307
题目
给一些点,求包含这些点的面积最小的和周长最小的长方形的面积。
题解
显然,包含这些点就是包含这些点的凸包,然后可以证明满足周长或面积最小的长方形一定过凸包的一条边。
不会直接证明= =可以用反证法
如果不过任意一条边,那么多边形与长方形就有三种关系:有2、3、4个点在长方形上
设其中的边长为已知,通过角度关系,可以证明长$AD$和宽$AB$的和(配角公式)与积(积化和差)与夹角成单峰函数,夹角范围是$[0,frac{pi}{2}]$,于是一定过一条边时取最小
参考一个求解多边形最小面积外接矩形的算法 - 图文 - 百度文库
于是可以参考旋转卡壳,选出到一条边距离最长的点,最左边的点和最右边的点,并且边旋转时,这些点也会跟着旋转,于是可以降到$O(n)$
加上凸包$O(nlog n)$
AC代码
#include<cstdio> #include<cmath> #include<cctype> #include<map> #include<algorithm> #include<vector> #include<cstring> using namespace std; #define REP(i,a,b) for(register int i=(a); i<(b); i++) #define REPE(i,a,b) for(register int i=(a); i<=(b); i++) #define PERE(i,a,b) for(register int i=(a); i>=(b); i--) #ifdef sahdsg #define DBG(...) printf(__VA_ARGS__),fflush(stdout) #else #define DBG(...) (void)0 #endif template<class T> inline void read(T&x) { static int si; static char ch; x=0,si=1; do ch=getchar(); while(!isdigit(ch)&&ch!='-');if(ch=='-') si=-1, ch=getchar(); while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}x*=si; } template<class T, class...A> inline void read(T&t,A&...a){read(t); read(a...);} #define D Point #define CD const Point template<class T,int Z> struct Arr { T data[Z];int n; inline T& operator[](int a) {return data[a];} inline void push(const T&x) {data[n++]=x;} inline void pop() {n--;} }; #define EPS 1e-9 inline int dcmp(double x) {return fabs(x)<EPS?0:(x<0?-1:1);} struct Point { double x,y; }; bool operator<(CD&l, CD&r) {return dcmp(l.x-r.x)<0 || (dcmp(l.x-r.x)==0 && dcmp(l.y-r.y)<0);} bool operator==(CD&l, CD&r) {return dcmp(l.x-r.x)==0 && dcmp(l.y-r.y)==0;} D operator+(CD&l, CD&r) {return (D){l.x+r.x,l.y+r.y};} D operator-(CD&l, CD&r) {return (D){l.x-r.x,l.y-r.y};} D operator/(CD&l, double a) {return (D){l.x/a,l.y/a};} D operator*(CD&l, double a) {return (D){l.x*a,l.y*a};} D operator*(double a, CD&l) {return (D){l.x*a,l.y*a};} double dot(CD&l, CD&r) {return l.x*r.x+l.y*r.y;} double cross(CD&l, CD&r) {return l.x*r.y-l.y*r.x;} double len(CD&l) {return sqrt(l.x*l.x+l.y*l.y);} #undef D #undef CD #define MAXN 100007 typedef Arr<Point,MAXN> Plg; void convex(Plg&p, Plg &ch) { sort(p.data,p.data+p.n); int &m=ch.n; m=0; REP(i,0,p.n) { while(m>1 && cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--;ch[m++]=p[i];} int k=m; PERE(i,p.n-2,0) {while(m>k && cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--;ch[m++]=p[i];} if(p.n>1) m--; } double area(Plg&p) { double ans=0; REP(i,0,p.n) { ans+=cross(p[i],p[(i+1)%p.n]); } return ans; } Plg a,b; int main() { int n; while(~scanf("%d", &n) && n) { REP(i,0,n) { scanf("%lf%lf", &a[i].x, &a[i].y); } a.n=n; convex(a,b); double S=2e33,L=2e33; b[b.n]=b[0]; int v=1; int lm, rm=0; bool fi=false; REP(u,0,b.n) { while(1) { double dt=cross(b[u+1]-b[u],b[v+1]-b[v]); if(dt<=0) { break; } v++; if(v>=b.n) v=0; } if(!fi) fi=true,lm=v; while(1) { double ds=dot(b[u+1]-b[u],b[lm+1]-b[lm]); if(ds>=0) break; lm++; if(lm>=b.n) lm=0; } while(1) { double ds=dot(b[u+1]-b[u],b[rm+1]-b[rm]); if(ds<=0) break; rm=(rm+1)%b.n; if(rm>=b.n) rm=0; } double z=len(b[u+1]-b[u]); double l=dot(b[u+1]-b[u],b[rm]-b[lm])/z; double h=cross(b[u+1]-b[u],b[v]-b[u])/z; S=min(S,l*h); L=min(L,(l+h)*2); } printf("%.2lf %.2lf ",S,L); } }