ナンセンス
これは、凸包を見つけるために使用されるアルゴリズムです。
前部チーズクロス製品。これを知った後、2つのベクトル間の回転関係を判断できます。
たとえば、if \ vec aa時計回りに180度180度回転1 8 0 ° within \ vec b以内にbを取得できますb、次にbを呼び出す⃗\ vec bb在a v \ vec aa 右側、それ以外の場合は左側または同一線上。
正题
このグラハムスキャン方法は実際には非常に理解しやすく、主に次の手順があります。
- これに基づいて最小の縦座標と最小の横座標を持つポイントを見つけます。このポイントは凸包上にある必要があることがわかり、このポイントを原点として座標系を再確立します。
- すべてのポイントを極角で最小から最大に並べ替えます。極角はこのポイントから原点までの線分であり、xxx軸の夾角。同じ極角が正面の原点に近く配置されます
- スタックを維持し、現在の凸包のポイントを記録してから、各ポイントを順番に配置してみます。
ポイントはステップ3 33.この試みを行う方法。
極角度で並べ替えた後、実際にはすべてのポイントを反時計回りに並べ替えることと同じであることがわかります。凸包にはプロパティがあります。つまり、凸包上の隣接する2つの点a、ba、bに対してA 、b、すべての点が直線上にあるab ab左 B、または線分上のAB ABa b、つまり、ab ab右側 bが点在することはできません。
したがって、a、ba、bA 、bは、この時点でスタックの最上部にある2つのポイントで、新しいポイントxxが挿入されるたびにx(xxの場合)x 在 a b ab a b右または直線ab aba bはオンラインとオフラインのセグメントab abです。a b、次にbbbは凸包の点ではなく、bbbキックオフ、1つのポイントまたはxxになるまでプロセスを繰り返しますx 在 a b ab a bは左側、次にポイントxxxがスタックに追加されます。
絵を描く、それだけです(xxx 在 a b ab B左側):
テンプレート質問ポータル
コードは以下のように表示されます:
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 100010
#define db double
#define inf 999999999
int n,m=0,t=0;
struct point{
db x,y; point(db xx=0,db yy=0):x(xx),y(yy){
}
point operator -(const point &b){
return point(x-b.x,y-b.y);}
db operator *(const point &b){
return x*b.y-y*b.x;}
};
point a[maxn],b[maxn],S,zhan[maxn];
db dis(point x,point y){
return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));}
bool cmp(point x,point y){
return x*y>0||(x*y==0&&dis(S,x)<dis(S,y));}
int main()
{
scanf("%d",&n);S.y=inf;
for(int i=1;i<=n;i++){
scanf("%lf %lf",&a[i].x,&a[i].y);
if(a[i].y<S.y||(S.y==a[i].y&&a[i].x<S.x)){
if(S.y!=inf)b[++m]=S;
S=a[i];
} else b[++m]=a[i];
}
for(int i=1;i<=m;i++)b[i].x-=S.x,b[i].y-=S.y;
sort(b+1,b+m+1,cmp);
zhan[++t]=S=point(0,0); zhan[++t]=b[1];
for(int i=2;i<=m;i++){
while(t>1&&(zhan[t]-zhan[t-1])*(b[i]-zhan[t-1])<=0)t--;
zhan[++t]=b[i];
}
db ans=dis(zhan[t],zhan[1]);
for(int i=1;i<t;i++)ans+=dis(zhan[i],zhan[i+1]);
printf("%.2lf",ans);
}