旋转卡壳
简析
利用了对踵点对,小心平行
例题
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;
}