csdn没有自动保存吗??我完美的学习计划被躺进医院打破了,等我出来笔记本没电了之前写的啥都没了,好气哦。
有 2 4 2^4 24种读音的神奇算法,似乎是读作xuán zhuǎn qiǎ ké,顾名思义就是拿两条线旋转着去卡一个凸壳。
与两条平行线相切的两个凸包上的点。
国外大佬证出对踵点对最多 3 ∗ n 2 \frac{3*n}{2} 23∗n对。
利用旋转卡壳可以在 O ( n ) O(n) O(n)时间内求出我们需要的一些对踵点对。
点点对踵都可以转化成点边对踵,具体我们每个点取它和它下一个点组成的边为它的边,如果两点 i , j i,j i,j对踵, i i i的边不和 j j j对踵,那么 j j j的边一定和 i i i对踵(画图易知)。
我们考虑给每个点的边找一个对踵点,边的对踵点在凸包上显然可以三分,用叉积判面积即可,我们要求n条边的对踵点,且这些点具有单调性,所以并不用三分,双指针扫即可。
注意的是,凸包上有两条边相互平行时,边边对踵会使边的对踵点多达两个,叉积时当面积不变时不再转会得到对角线上较远那对,而继续转会得到相邻较近那对。
直径一定出现在对踵点间。若需求平面最远点对,先求凸包即可。
POJ-2187 Beauty Contest
求平面最远点对距离的平方,模板题
//Achen
#include
#include
#include
#include
#include
#include
#include
#include
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=50007;
typedef long long LL;
typedef double db;
using namespace std;
int n,top;
LL ans;
template<typename T>void read(T &x) {
char ch=getchar(); T f=1; x=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
struct pt{
LL x,y;
pt(){}
pt(LL x,LL y):x(x),y(y){}
friend bool operator <(const pt&A,const pt&B) {
return A.x<B.x||(A.x==B.x&&A.y<B.y);
}
}p[N],ham[N];
pt operator -(const pt&A,const pt&B) { return pt(A.x-B.x,A.y-B.y); }
LL dot(const pt&A,const pt&B) { return A.x*B.x+A.y*B.y; }
LL cross(const pt&A,const pt&B) { return A.x*B.y-A.y*B.x; }
LL lenth(const pt&A) { return dot(A,A); }
bool cmp(const pt&A,const pt&B) {
return cross(A-p[1],B-p[1])<0||(cross(A-p[1],B-p[1])==0&&lenth(A-p[1])<lenth(B-p[1]));
}
void get_ham(int n) {
For(i,2,n) if(p[i]<p[1]) swap(p[i],p[1]);
sort(p+2,p+n+1,cmp); top=0;
ham[top++]=p[1];
For(i,2,n) {
while(top>=2&&cross(p[i]-ham[top-2],ham[top-1]-ham[top-2])<=0) top--;
ham[top++]=p[i];
}
}
void RC(int top) {
ham[top]=ham[0];
int j=1;
For(i,0,top-1) {
while(cross(ham[j]-ham[i],ham[(i+1)%top]-ham[i])<cross(ham[(j+1)%top]-ham[i],ham[(i+1)%top]-ham[i])) j=(j+1)%top;
ans=max(ans,lenth(ham[i]-ham[j]));
}
}
int main() {
read(n);
For(i,1,n) { read(p[i].x); read(p[i].y); }
get_ham(n);
RC(top);
printf("%lld\n",ans);
Formylove;
}
宽定义为平行切线间的最小距离。对每条边的对踵点求点到直线的距离即可。
并没有找到题。
类比凸多边形的直径,在两个多边形上转。没有找到题,可参考下题。
类比凸多边形的宽,在两个多边形上转。
首先最大最小距离仍出现在对踵点上。
过一个多边形做切线,另一个多边形可能会有两条与之平行的切线,给切线定向,只考虑两条反向的切线,最大最小距离一定出现在反向切线上。
找反向切线仍是用叉积找到最大面积,之后双指针找每条边的对踵点,用点到线段的距离更新答案。
上面说过”如果两点 i , j i,j i,j对踵, i i i的边不和 j j j对踵,那么 j j j的边一定和 i i i对踵“,在这里就要在两个凸包上分别转一次。
我的写法是对于第一条边暴力找对踵点,然后边边对踵时,把两个点到线段的距离取min。
POJ-3608 Bridge Across Islands
//Achen
#include
#include
#include
#include
#include
#include
#include
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=10007;
typedef long long LL;
typedef double db;
using namespace std;
int n,m;
db ans;
template<typename T> void read(T &x) {
T f=1; x=0; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
#define eps 1e-10
int dcmp(db x) { return fabs(x)<eps?0:(x>0?1:-1); }
struct pt{
db x,y;
pt(){}
pt(db x,db y):x(x),y(y){}
friend bool operator < (const pt &A,const pt &B) {
return A.x<B.x||(A.x==B.x&&A.y<B.y);
}
}pbs,pa[N],pb[N];
pt operator -(const pt &A,const pt &B) { return pt(A.x-B.x,A.y-B.y); }
db cross(const pt &A,const pt &B) { return A.x*B.y-A.y*B.x; }
db dot(const pt &A,const pt &B) { return A.x*B.x+A.y*B.y; }
db lenth(pt A) { return dot(A,A); }
db dis_to_line(pt C,pt A,pt B) {
db D=sqrt(lenth(A-B));
if(dcmp(D)==0) return sqrt(lenth(A-C));
else return fabs(cross(C-A,B-A))/D;
}
db dis_to_seg(pt C,pt A,pt B) {
if(dcmp(cross(C-A,B-A))==0) {
db lx=min(A.x,B.x),rx=max(A.x,B.x),ly=min(A.y,B.y),ry=max(A.y,B.y);
if(dcmp(C.x-lx)>=0&&dcmp(C.x-rx)<=0&&dcmp(C.y-ly)>=0&&dcmp(C.y-ry)<=0) return 0;
else return min(sqrt(lenth(C-A)),sqrt(lenth(C-B)));
}
if(dcmp(dot(C-A,B-A))>=0&&dcmp(dot(C-B,A-B))>=0) return dis_to_line(C,A,B);
else return min(sqrt(lenth(C-A)),sqrt(lenth(C-B)));
}
bool cmp(const pt&A,const pt&B) {
return dcmp(cross(A-pbs,B-pbs))<0||(dcmp(cross(A-pbs,B-pbs))==0&&dcmp(lenth(A-pbs)-lenth(B-pbs))<0);
}
void RC(int n,int m,pt a[],pt b[]){
int j=0;
a[n]=a[0];
For(i,1,m-1) if(dcmp(cross(b[j]-a[0],a[1]-a[0])-cross(b[i]-a[0],a[1]-a[0]))<0) j=i;
For(i,0,n-1) {
while(i&&(dcmp(cross(b[j%m]-a[i],a[i+1]-a[i])-cross(b[(j+1)%m]-a[i],a[i+1]-a[i]))<0)) j++;
ans=min(ans,dis_to_seg(b[j%m],a[i],a[i+1]));
if(dcmp(cross(b[j%m]-a[i],a[i+1]-a[i])-cross(b[(j+1)%m]-a[i],a[i+1]-a[i]))==0)
ans=min(ans,dis_to_seg(b[(j+1)%m],a[i],a[i+1]));
}
}
int main() {
//freopen("std.in","r",stdin);
//freopen("1.out","w",stdout);
for(;;) {
read(n); read(m);
if(!n&&!m) break;
For(i,0,n-1) scanf("%lf%lf",&pa[i].x,&pa[i].y);
For(i,0,m-1) scanf("%lf%lf",&pb[i].x,&pb[i].y);
For(i,1,n-1) if(pa[i]<pa[0]) swap(pa[i],pa[0]);
For(i,1,m-1) if(pb[i]<pb[0]) swap(pb[i],pb[0]);
pbs=pa[0]; sort(pa+1,pa+n,cmp);
pbs=pb[0]; sort(pb+1,pb+m,cmp);
ans=1e9;
RC(n,m,pa,pb);
RC(m,n,pb,pa);
printf("%.5lf\n",ans);
}
Formylove;
}
首先有结论,最小面积外接矩形至少有一条边与多边形的边重合。(我不会证,求助QAQ)
那么旋转卡壳枚举所有边点/边边对踵,有了一组平行线可以找到另一组,容易得到一个n^2的做法。
实际上我们需要两组相互垂直的平行线,可以做两次旋转卡壳,且这两组旋转卡壳的单调性是同步的,可以一起转,复杂度依旧为O(n)。
只需要第一组用叉积判断第二组用点积判断。
UVA-10173 Smallest Bounding Rectangle
//Achen
#include
#include
#include
#include
#include
#include
#include
#include
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=50007;
typedef long long LL;
typedef double db;
using namespace std;
int n,top;
db ans;
template<typename T>void read(T &x) {
char ch=getchar(); T f=1; x=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
#define eps 1e-8
int dcmp(db x) { return fabs(x)<eps?0:(x>0?1:-1); }
struct pt{
db x,y;
pt(){}
pt(db x,db y):x(x),y(y){}
friend bool operator <(const pt&A,const pt&B) {
return A.x<B.x||(A.x==B.x&&A.y<B.y);
}
}p[N],ham[N];
pt operator -(const pt&A,const pt&B) { return pt(A.x-B.x,A.y-B.y); }
db dot(const pt&A,const pt&B) { return A.x*B.x+A.y*B.y; }
db cross(const pt&A,const pt&B) { return A.x*B.y-A.y*B.x; }
db lenth(const pt&A) { return sqrt(dot(A,A)); }
db node_to_line(pt C,pt A,pt B) {
return fabs(cross(C-A,B-A))/lenth(A-B);
}
bool cmp(const pt&A,const pt&B) {
return dcmp(cross(A-p[1],B-p[1]))<0||(dcmp(cross(A-p[1],B-p[1]))==0&&dcmp(lenth(A-p[1])-lenth(B-p[1]))<0);
}
void get_ham(int n) {
For(i,2,n) if(p[i]<p[1]) swap(p[i],p[1]);
sort(p+2,p+n+1,cmp); top=0;
ham[top++]=p[1];
For(i,2,n) {
while(top>=2&&dcmp(cross(p[i]-ham[top-2],ham[top-1]-ham[top-2]))<=0) top--;
ham[top++]=p[i];
}
}
void RC(int top) {
ham[top]=ham[0];
int j=1,k=1,l=1;
For(i,0,top-1) {
while(dcmp(cross(ham[j%top]-ham[i],ham[i+1]-ham[i])-cross(ham[(j+1)%top]-ham[i],ham[i+1]-ham[i]))<0) j++;
k=max(k,i+1); l=max(l,j);
while(dcmp(dot(ham[k%top]-ham[i+1],ham[i]-ham[i+1])-dot(ham[(k+1)%top]-ham[i+1],ham[i]-ham[i+1]))>0) k++;
while(dcmp(dot(ham[l%top]-ham[i],ham[i+1]-ham[i])-dot(ham[(l+1)%top]-ham[i],ham[i+1]-ham[i]))>0) l++;
db d=lenth(ham[i+1]-ham[i]);
db L=fabs(dot(ham[k%top]-ham[i+1],ham[i]-ham[i+1]))/d+fabs(dot(ham[l%top]-ham[i],ham[i+1]-ham[i]))/d+d;
db D=node_to_line(ham[j%top],ham[i],ham[i+1]);
ans=min(ans,L*D);
}
if(top<3) ans=0;
}
int main() {
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
for(;;) {
read(n);
if(!n) break;
For(i,1,n) scanf("%lf%lf",&p[i].x,&p[i].y);
get_ham(n);
ans=1e9;
RC(top);
printf("%.4lf\n",ans);
}
Formylove;
}
首先有和最小面积同样的结论(菜如我依然不会证),于是做法同上。
多数情况下这两个矩形是一样的,但不是全部。