跳到主要内容

凸包

参考资料

Andrew 算法

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

const int N=100005;
struct Point
{
double x,y;
Point(){}
Point(double _x,double _y):x(_x),y(_y){}
Point operator-(Point B){return Point(x-B.x,y-B.y);}
}a[N];
double Dis(Point A,Point B){return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));}
double Cross(Point A,Point B){return A.x*B.y-A.y*B.x;}
bool cmp(Point A,Point B)
{
return A.x!=B.x?A.x<B.x:A.y<B.y;
}
int s[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].y;
}
sort(a+1,a+n+1,cmp);
int m=0;
for(int i=1;i<=n;i++)
{
while(m>1&&Cross(a[s[m]]-a[s[m-1]],a[i]-a[s[m]])<=0)m--;
s[++m]=i;
}
int t=m;
for(int i=n-1;i>=1;i--)
{
while(m>t&&Cross(a[s[m]]-a[s[m-1]],a[i]-a[s[m]])<=0)m--;
s[++m]=i;
}
if(m>1)m--;
double ans=0;
for(int i=1;i<=m;i++)ans+=Dis(a[s[i]],a[s[i%m+1]]);
cout<<fixed<<setprecision(2)<<ans<<'\n';
return 0;
}

Graham 扫描法

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

const int N=100005;
struct Point
{
double x,y;
Point(){}
Point(double _x,double _y):x(_x),y(_y){}
Point operator-(Point B){return Point(x-B.x,y-B.y);}
}a[N];
double Dis(Point A,Point B){return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));}
double Cross(Point A,Point B){return A.x*B.y-A.y*B.x;}
bool cmp(Point A,Point B)
{
double t=Cross(A-a[1],B-a[1]);
return t!=0?t>0:Dis(A,a[1])<Dis(B,a[1]);
}
int s[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].y;
if(a[i].y!=a[1].y?a[i].y<a[1].y:a[i].x<a[1].x)swap(a[i],a[1]);
}
sort(a+2,a+n+1,cmp);
int m=0;
for(int i=1;i<=n;i++)
{
while(m>1&&Cross(a[s[m]]-a[s[m-1]],a[i]-a[s[m]])<=0)m--;
s[++m]=i;
}
double ans=0;
for(int i=1;i<=m;i++)ans+=Dis(a[s[i]],a[s[i%m+1]]);
cout<<fixed<<setprecision(2)<<ans<<'\n';
return 0;
}

例题

洛谷 P2742 [USACO5.1] 圈奶牛Fencing the Cows /【模板】二维凸包

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

const int N=100005;
struct Point
{
double x,y;
Point(){}
Point(double _x,double _y):x(_x),y(_y){}
Point operator-(Point B){return Point(x-B.x,y-B.y);}
}a[N];
double Dis(Point A,Point B){return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));}
double Cross(Point A,Point B){return A.x*B.y-A.y*B.x;}
bool cmp(Point A,Point B)
{
return A.x!=B.x?A.x<B.x:A.y<B.y;
}
int s[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].y;
}
sort(a+1,a+n+1,cmp);
int m=0;
for(int i=1;i<=n;i++)
{
while(m>1&&Cross(a[s[m]]-a[s[m-1]],a[i]-a[s[m]])<=0)m--;
s[++m]=i;
}
int t=m;
for(int i=n-1;i>=1;i--)
{
while(m>t&&Cross(a[s[m]]-a[s[m-1]],a[i]-a[s[m]])<=0)m--;
s[++m]=i;
}
if(m>1)m--;
double ans=0;
for(int i=1;i<=m;i++)ans+=Dis(a[s[i]],a[s[i%m+1]]);
cout<<fixed<<setprecision(2)<<ans<<'\n';
return 0;
}

洛谷 P2116 城墙

在城堡外修建一堵围墙,要求围墙离城堡的最近距离不能少于 LL,求出最少需要修建多长的围墙。

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

const double pi=acos(-1);
const int N=1005;
struct Point
{
double x,y;
Point(){}
Point(double _x,double _y):x(_x),y(_y){}
Point operator-(Point B){return Point(x-B.x,y-B.y);}
}a[N];
double Dis(Point A,Point B){return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));}
double Cross(Point A,Point B){return A.x*B.y-A.y*B.x;}
bool cmp(Point A,Point B)
{
return A.x!=B.x?A.x<B.x:A.y<B.y;
}
int s[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,l;
cin>>n>>l;
for(int i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].y;
}
sort(a+1,a+n+1,cmp);
int m=0;
for(int i=1;i<=n;i++)
{
while(m>1&&Cross(a[s[m]]-a[s[m-1]],a[i]-a[s[m]])<=0)m--;
s[++m]=i;
}
int t=m;
for(int i=n-1;i>=1;i--)
{
while(m>t&&Cross(a[s[m]]-a[s[m-1]],a[i]-a[s[m]])<=0)m--;
s[++m]=i;
}
if(m>1)m--;
double ans=l*pi*2;
for(int i=1;i<=m;i++)ans+=Dis(a[s[i]],a[s[i%m+1]]);
cout<<int(ans+0.5)<<'\n';
return 0;
}

洛谷 P3829 [SHOI2012] 信用卡凸包

给定平面上一些规格相同的信用卡,求其凸包的周长。

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

const double pi=acos(-1);
const int N=10005;
struct Point
{
double x,y;
Point(){}
Point(double _x,double _y):x(_x),y(_y){}
Point operator+(Point B){return Point(x+B.x,y+B.y);}
Point operator-(Point B){return Point(x-B.x,y-B.y);}
}a[N<<2];
double Dis(Point A,Point B){return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));}
double Cross(Point A,Point B){return A.x*B.y-A.y*B.x;}
Point rotate(Point A,double t){return Point(A.x*cos(t)-A.y*sin(t),A.x*sin(t)+A.y*cos(t));}
bool cmp(Point A,Point B)
{
return A.x!=B.x?A.x<B.x:A.y<B.y;
}
int s[N<<2];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
double A,B,r;
cin>>n>>A>>B>>r;
A=A/2-r;B=B/2-r;
for(int i=1;i<=n;i++)
{
Point P;
double t;
cin>>P.x>>P.y>>t;
a[i*4-3]=rotate({B,A},t)+P;
a[i*4-2]=rotate({B,-A},t)+P;
a[i*4-1]=rotate({-B,A},t)+P;
a[i*4]=rotate({-B,-A},t)+P;
}
n<<=2;
sort(a+1,a+n+1,cmp);
int m=0;
for(int i=1;i<=n;i++)
{
while(m>1&&Cross(a[s[m]]-a[s[m-1]],a[i]-a[s[m]])<=0)m--;
s[++m]=i;
}
int t=m;
for(int i=n-1;i>=1;i--)
{
while(m>t&&Cross(a[s[m]]-a[s[m-1]],a[i]-a[s[m]])<=0)m--;
s[++m]=i;
}
if(m>1)m--;
double ans=r*pi*2;
for(int i=1;i<=m;i++)ans+=Dis(a[s[i]],a[s[i%m+1]]);
cout<<fixed<<setprecision(2)<<ans<<'\n';
return 0;
}

题解

洛谷 UVA1303 Wall

给定平面上 nn 个点,求出最短的包围所有点的轮廓且满足任意点到轮廓的距离不小于给定的 LL