旋转卡壳

简析

利用了对踵点对,小心平行

例题

A. 【例题1】最远点对 - 「计算几何」第3章 旋转卡壳 - 金牌导航 - 课程 - YbtOJ

最远点对一定是对踵点对中的两个点

所以只需要枚举所有的对踵点对即可,枚举所有的边求对踵点即可,即枚举所有的边求离边最远的点即可或者用平行线去切,使得多边形在这组平行线间

可以发现这具有单峰性,且随着边的逆时针,点也逆时针相应,所以\(O(n)\)

注意:最好每次对4个点(i,j,p,p+1)两两求距离取max(据说否则平行会出错?)

#include<bits/stdc++.h>
#define db double
using namespace std;

const int N=5e4+5;
int n,top;
struct Po{int x,y; }a[N],st[N];
inline Po operator -(Po i,Po j) {
	return (Po){i.x-j.x,i.y-j.y};
}
inline int operator *(Po i,Po j) {
	return i.x*j.y-i.y*j.x;
}
inline int dis(Po i) {
	return i.x*i.x+i.y*i.y;
}
inline bool cmp(Po i,Po j) {
	int t=(i-a[1])*(j-a[1]);
	return t>0||t==0&&dis(i-a[1])<dis(j-a[1]);
}
void tubao(Po *a) {
	for(int i=2;i<=n;i++) {
		if(a[1].x>a[i].x||a[1].x==a[i].x&&a[1].y>a[i].y) {
			swap(a[1],a[i]);
		}
	}
	sort(a+2,a+n+1,cmp);
	st[++top]=a[1],st[++top]=a[2];
	for(int i=3;i<=n;i++) {
		while(top>1&&(st[top]-st[top-1])*(a[i]-st[top])<=0) top--;
		st[++top]=a[i];
	}
}
int xzkq(Po *a,int n) {
	a[n+1]=a[1];
	int r=2;
	int ret=0;
	for(int i=1;i<=n;i++) {
		while((a[i+1]-a[i])*(a[r]-a[i])<(a[i+1]-a[i])*(a[r+1]-a[i])) {
			r=r%n+1; 
		}
		ret=max(ret,max(dis(a[r]-a[i]),dis(a[r]-a[i+1]))); 
	}
	return ret;
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d%d",&a[i].x,&a[i].y);
	}
	tubao(a);
	printf("%d\n",xzkq(st,top));
	return 0;
}

例题1

3608 -- Bridge Across Islands (poj.org)

先找出两个平行向量,使得凸包分别在向量右边,然后分别逆时针转一遍,与两个凸包分别重合(做两遍),为小心平行的情况,每条线要与对踵点和其相邻两个点求一下距离

//#include<bits/stdc++.h>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define db long double
const db eps=1e-10;
using namespace std;

const int N=1e5+5;
int n,m,top;
struct Po{db x,y; }a[N],b[N],st[N];
struct Li{Po s,t; };
inline db operator *(Po i,Po j) {
	return i.x*j.y-i.y*j.x;
}
inline Po operator -(Po i,Po j) {
	Po ret; ret.x=i.x-j.x,ret.y=i.y-j.y;
	return ret;
}
inline db dot(Po i,Po j) {
	return i.x*j.x+i.y*j.y;
}
inline db dis(Po i) {
	return sqrt(i.x*i.x+i.y*i.y);
}
inline db Dist(Li i,Po j) {
	if(dot(i.s-i.t,j-i.t)>=0&&dot(i.t-i.s,j-i.s)>=0) {
		return abs((i.s-j)*(i.t-j))/dis(i.t-i.s);
	} 
	return min(dis(i.s-j),dis(i.t-j));
}
inline bool equ(db x,db y) {
	return abs(x-y)<=eps;
}
inline db ask(Po *a,int n,Po *b,int m) {
	b[m+1]=b[1]; db ret=1e9;
	for(int i=1,j=n,p=m,q=1,r=2;i<=n;j=i,i++) { //(j,i)(p,q,r)
		while(!((b[q]-b[p])*(a[j]-a[i])>=0&&(a[j]-a[i])*(b[r]-b[q])>=0)) {
			p=p%m+1,q=q%m+1,r=r%m+1;
		}
		Li t; t.s=a[j],t.t=a[i];
		ret=min(ret,Dist(t,b[p]));
		ret=min(ret,Dist(t,b[q]));
		ret=min(ret,Dist(t,b[r]));
	}
	return ret;
}

Po s0;
inline bool cmp(Po i,Po j) {
	db t=(i-s0)*(j-s0);
	return t>eps||equ(t,0)&&dis(i-s0)+eps<dis(j-s0);
}
void tubao(Po *a,int &n) {
	for(int i=2;i<=n;i++) {
		if(a[1].x>a[i].x||a[1].x==a[i].x&&a[1].y>a[i].y) swap(a[1],a[i]);
	}
	s0=a[1];
	sort(a+2,a+n+1,cmp);
	top=0; st[++top]=a[1],st[++top]=a[2];
	for(int i=3;i<=n;i++) {
		while(top>1&&(st[top]-st[top-1])*(a[i]-st[top])<eps) top--;
		st[++top]=a[i];
	}
	n=top;
	for(int i=1;i<=n;i++) a[i]=st[i];
}
int main() {
	while(scanf("%d%d",&n,&m),n||m) {
		for(int i=1;i<=n;i++) {
			scanf("%Lf%Lf",&a[i].x,&a[i].y);
		}
		tubao(a,n);
		for(int i=1;i<=m;i++) {
			scanf("%Lf%Lf",&b[i].x,&b[i].y);
		}
		tubao(b,m);
		printf("%.5Lf\n",min(ask(a,n,b,m),ask(b,m,a,n))); 
	}
	return 0;
}

例题2

C. 最大三角形 - 「计算几何」第3章 旋转卡壳 - 金牌导航 - 课程 - YbtOJ

feel一下:三角形三个点在凸包上(如果某个点不在,则定能找出一个比它大)

然后枚举线段,求出离线段最远的点,相平行

时间复杂度\(O(n^2)\)可以过

平不平行无所谓

#include<bits/stdc++.h>
#define db double
using namespace std;

const int N=5e4+5;
int n;
struct Po{int x,y; }a[N],s0,st[N];
inline int operator *(Po i,Po j) {
	return i.x*j.y-i.y*j.x;
}
inline Po operator -(Po i,Po j) {
	return (Po){i.x-j.x,i.y-j.y};
}
inline db dis(Po i) {
	return sqrt(i.x*i.x+i.y*i.y);
}
inline bool cmp(Po i,Po j) {
	int t=(i-s0)*(j-s0);
	return t>0||t==0&&dis(i-s0)<dis(j-s0);
}
void graham(Po *a,int &n) {
	for(int i=2;i<=n;i++) {
		if(a[1].x>a[i].x||a[1].x==a[i].x&&a[1].y>a[i].y) swap(a[1],a[i]);
	}
	s0=a[1];
	sort(a+1,a+n+1,cmp);
	int top=0; st[++top]=a[1],st[++top]=a[2];
	for(int i=3;i<=n;i++) {
		while(top>1&&(st[top]-st[top-1])*(a[i]-st[top])<=0) top--;
		st[++top]=a[i];
	}
	n=top;
	for(int i=1;i<=n;i++) a[i]=st[i];
}
inline db ask(int t) {
	db ret=0;
	if(n-t-1>0) {
		for(int i=1,j=1+t,p=j,q=j%n+1,r=(j+1)%n+1;i<=n;i++,j=j%n+1) { //(i,j),(p,q,r)
			while(!((a[q]-a[p])*(a[i]-a[j])>=0&&(a[i]-a[j])*(a[r]-a[q])>=0)) {
				p=p%n+1,q=q%n+1,r=r%n+1;
			}
			ret=max(ret,(db)abs((a[q]-a[i])*(a[j]-a[i]))*0.5);
		}
	}
	if(t>1) {
		for(int i=1,j=1+t,p=1,q=2,r=3;i<=n;i++,j=j%n+1) {
			while(!((a[q]-a[p])*(a[j]-a[i])>=0&&(a[j]-a[i])*(a[r]-a[q])>=0)) {
				p=p%n+1,q=q%n+1,r=r%n+1;
			}
			ret=max(ret,(db)abs((a[q]-a[i])*(a[j]-a[i]))*0.5);
		}
	}
	return ret;
}
int main() {
	while(scanf("%d",&n),n!=-1) {
		for(int i=1;i<=n;i++) {
			scanf("%d%d",&a[i].x,&a[i].y);
		}
		graham(a,n);
		db ans=0;
		for(int i=1;i<=n/2;i++) {
			ans=max(ans,ask(i));
		}
		printf("%.2lf\n",ans);
	}
	return 0;
}

例题3

[P4166 SCOI2007]最大土地面积 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

和上体一样,显然4个点在凸包上,枚举对角线,求出最大面积

WA了一次:向上题一样分开求上下。。然后加起来

#include<bits/stdc++.h>
#define db double
using namespace std;

const int N=5e4+5;
int n;
struct Po{db x,y; }a[N],s0,st[N];
inline db operator *(Po i,Po j) {
	return i.x*j.y-i.y*j.x;
}
inline Po operator -(Po i,Po j) {
	return (Po){i.x-j.x,i.y-j.y};
}
inline db dis(Po i) {
	return sqrt(i.x*i.x+i.y*i.y);
}
inline bool cmp(Po i,Po j) {
	db t=(i-s0)*(j-s0);
	return t>0||t==0&&dis(i-s0)<dis(j-s0);
}
void graham(Po *a,int &n) {
	for(int i=2;i<=n;i++) {
		if(a[1].x>a[i].x||a[1].x==a[i].x&&a[1].y>a[i].y) swap(a[1],a[i]);
	}
	s0=a[1];
	sort(a+1,a+n+1,cmp);
	int top=0; st[++top]=a[1],st[++top]=a[2];
	for(int i=3;i<=n;i++) {
		while(top>1&&(st[top]-st[top-1])*(a[i]-st[top])<=0) top--;
		st[++top]=a[i];
	}
	n=top;
	for(int i=1;i<=n;i++) a[i]=st[i];
}
inline db ask(int t) {
	db ret=0;
	for(int i=1,j=1+t,p=j,q=j%n+1,r=(j+1)%n+1,p1=1,q1=2,r1=3;i<=n;i++,j=j%n+1) { //(i,j),(p,q,r)
		while(!((a[q]-a[p])*(a[i]-a[j])>=0&&(a[i]-a[j])*(a[r]-a[q])>=0)) {
			p=p%n+1,q=q%n+1,r=r%n+1;
		}
		while(!((a[q1]-a[p1])*(a[j]-a[i])>=0&&(a[j]-a[i])*(a[r1]-a[q1])>=0)) {
			p1=p1%n+1,q1=q1%n+1,r1=r1%n+1;
		}
		ret=max(ret,(db)abs((a[q]-a[i])*(a[j]-a[i]))*0.5+(db)abs((a[q1]-a[i])*(a[j]-a[i]))*0.5);
	}
	return ret;
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%lf%lf",&a[i].x,&a[i].y);
	}
	graham(a,n);
	db ans=0;
	for(int i=2;i<=n/2;i++) {
		ans=max(ans,ask(i));
	}
	printf("%.3lf\n",ans);
	return 0;
}

例题4

[P3187 HNOI2007]最小矩形覆盖 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Feel一下:最小周长还是最小面积都有一条边与凸包重合

枚举边维护3个点即可

WA了一次:输出-0.0

#include<bits/stdc++.h>
#define db double
const db eps=1e-9;
using namespace std;

const int N=5e4+5;
int n;
struct Po{db x,y; }a[N],s0,st[N];
struct Li{Po s,t; };
inline db operator *(Po i,Po j) {
	return i.x*j.y-i.y*j.x;
}
inline Po operator *(Po i,db j) {
	return (Po){i.x*j,i.y*j};
}
inline Po operator -(Po i,Po j) {
	return (Po){i.x-j.x,i.y-j.y};
}
inline Po operator +(Po i,Po j) {
	return (Po){i.x+j.x,i.y+j.y};
}
inline db dis(Po i) {
	return sqrt(i.x*i.x+i.y*i.y);
}
inline db dist(Li i,Po j) {
//	printf("%.3lf\n",dis(i.s-i.t));
	return fabs((i.s-j)*(i.t-j))/dis(i.s-i.t);
}
inline db dot(Po i,Po j) {
	return i.x*j.x+i.y*j.y;
}
inline bool equ(db x,db y) {
	return fabs(x-y)<=eps;
}
inline bool cmp(Po i,Po j) {
	db t=(i-s0)*(j-s0);
	return t>eps||equ(t,0)&&dis(i-s0)<dis(j-s0);
}
void graham(Po *a,int &n) {
	for(int i=2;i<=n;i++) {
		if(a[1].y>a[i].y+eps||equ(a[1].y,a[i].y)&&a[1].x>a[i].x+eps) swap(a[1],a[i]);
	}
	s0=a[1];
	sort(a+2,a+n+1,cmp);
	int top=0; st[++top]=a[1],st[++top]=a[2];
	for(int i=3;i<=n;i++) {
		while(top>1&&(st[top]-st[top-1])*(a[i]-st[top])<=0) top--;
		st[++top]=a[i];
	}
	n=top;
	for(int i=1;i<=n;i++) a[i]=st[i];
}
inline Po turn(Po t) {
	return (Po){-t.y,t.x};
}
inline db ask() {
	db ret=2e9;
	Po t1,t2,t3,t4;
	for(int i=n,j=1,p1=j,q1=j%n+1,r1=(j+1)%n+1,p2=p1,q2=q1,r2=r1,p3=p1,q3=q1,r3=r1;j<=n;i=j,j++) { 
		//(i,j),(p,q,r)
		Po t=turn(a[j]-a[i]); 
		while(!((a[q1]-a[p1])*t>=0&&t*(a[r1]-a[q1])>=0)) {
			p1=p1%n+1,q1=q1%n+1,r1=r1%n+1;
		}
		t=turn(t);
		while(!((a[q2]-a[p2])*t>=0&&t*(a[r2]-a[q2])>=0)) {
			p2=p2%n+1,q2=q2%n+1,r2=r2%n+1;
		}
		t=turn(t);
		while(!((a[q3]-a[p3])*t>=0&&t*(a[r3]-a[q3])>=0)) {
			p3=p3%n+1,q3=q3%n+1,r3=r3%n+1;
		}
		db s=dist((Li){a[i],a[j]},a[q2])*fabs(dot(a[q1]-a[q3],a[i]-a[j]))/dis(a[i]-a[j]);
		if(ret>s) {
			ret=s;
			t=a[j]-a[i]; Po tt=a[q1]-a[j]; 
			t1=a[j]+t*(dot(t,tt)/dot(t,t));
			t=turn(t); tt=a[q2]-a[q1];
			t2=a[q1]+t*(dot(t,tt)/dot(t,t));
			t=turn(t); tt=a[q3]-a[q2];
			t3=a[q2]+t*(dot(t,tt)/dot(t,t));
			t=turn(t); tt=a[i]-a[q3];
			t4=a[q3]+t*(dot(t,tt)/dot(t,t));
			
		}
	}
	a[1]=t1,a[2]=t2,a[3]=t3,a[4]=t4; n=4;
	graham(a,n);
	printf("%.5lf\n",ret);
	for(int i=1;i<=4;i++) {
		if(fabs(a[i].x)<=eps)printf("%.5lf ",0.00);
			else printf("%.5lf ",a[i].x);	
		if(fabs(a[i].y)<=eps)printf("%.5lf\n",0.00);
			else printf("%.5lf\n",a[i].y);
	}
	return ret;
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%lf%lf",&a[i].x,&a[i].y);
	}
	graham(a,n);
	ask();
	return 0;
}
posted @ 2021-04-27 09:58  wwwsfff  阅读(92)  评论(0编辑  收藏  举报