ディレクトリ
計算幾何val.2
事前チーズ:凸包と基本操作
この紙貼り書き込み回転、クロス半平面は、最小の円オーバーレイコンテンツは留意すべきです
ジオメトリ単位構造ボード
不完全(私が知っています
struct point{
double x,y;
point(double x=0,double y=0): x(x),y(y){} //构造函数,非常方便
double operator*(point b){ //叉积
return x*b.y-y*b.x;
}
double operator^(point b){ //点积
return x*b.x+y*b.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);
}//向量运算
point operator*(double b){ //数乘
return point(x*b,y*b);
}
db dis(){ //模长
return sqrt(x*x+y*y);
}
}b[N];
int comp0(double x){
return fabs(x)<=eps?0:(x>0?1:-1);
}//判0,防止精度误差
struct line{
point p,v;//p起点,v终点 ,表示线段
double theta;
bool operator <(line y){
return comp0(theta-y.theta)==0?comp0((y.v-p)*(v-p))<0:comp0(theta-y.theta)<0;
}//排序,保证第一关键字是极角,第二关键字是与右边的距离(用叉积判相对关系,在左边的放后面)
};
point inter(line a,line b){//交点 intersection
point v1=a.v-a.p,v2=b.v-b.p;
return b.p+v2*(((b.p-a.p)*v1)/(v1*v2));
}//此处默认了没有平行的情况 , 判断线段有没有交点就是判断点是否在线段上,但是半平面交是直线(只是用线段表示)
立ち往生回転
\ [\大{\テキスト{↗スピントランスファー↘↗カード↘↗ハウジング(ko↗)}} \]
はい、非常に真
このアルゴリズムは、見つけるために使用された凸多角形の直径を
基本的な考え方
正接
頂点を通る線、多角形、この線の一方の側にあります
ヒールポイントに
平行線のペアを作成するポリゴン上の2点、全体の二行の間のポリゴン
探求
各側の反時計回りの列挙を考えると、この点からエッジ遠いを見つけ、この点の直径と、このエッジの頂点が可能な回答で構成されています
米国意外とポイントの出現順序が反時計回りであることがわかったことは、あなたがすることができます(O(n)を\)\得るために、
(もちろん、凸包必要(n)のログのn \ \ \を
注:必要に応じて個別のエッジ遠いポイントをあなたは3分の1(単峰性関数)を見つけることができます
テンプレート
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define db double
using namespace std;
struct point{
double x,y;
point(double x=0,double y=0): x(x),y(y){}
double operator*(point b){
return x*b.y-y*b.x;
}
point operator-(point b){
return point(x-b.x,y-b.y);
}
point operator+(point b){
return point(x+b.x,y+b.y);
}
db dis(){
return sqrt(x*x+y*y);
}
};
const int N = 50021;
point p[N],h[N];
int tp=0,stk[N],usd[N];
int cmp(point a,point b){
return a.x==b.x?a.y<b.y:a.x<b.x;
}
int n=0;
db Fabs(db a){
return a>0?a:-a;
}
db disl(point a,point b,point x){
return Fabs((a-x)*(b-x)/(a-b).dis());
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf",&p[i].x,&p[i].y);
}
sort(p+1,p+n+1,cmp);
stk[++tp]=1;
for(int i=2;i<=n;i++){
while(tp>1&&(p[stk[tp]]-p[stk[tp-1]])*(p[i]-p[stk[tp]])<=0) usd[stk[tp--]]=0;
usd[i]=1;stk[++tp]=i;
}
int ntp=tp;
for(int i=n-1;i>=1;i--){
if(!usd[i]){
while(tp>ntp&&(p[stk[tp]]-p[stk[tp-1]])*(p[i]-p[stk[tp]])<=0) usd[stk[tp--]]=0;
usd[i]=1;stk[++tp]=i;
}
}
for(int i=1;i<=tp;i++){
h[i]=p[stk[i]];
}
if(tp<=2){ //只有一个点
puts("0");
return 0;
}
if(tp==3){
printf("%.0lf\n",(h[1]-h[2]).dis()*(h[1]-h[2]).dis());
return 0;
}
db ans=0;
int t=1;
//注意最后一个点(就是1号点)要保留,因为后面有h[i+1],[t+1],方便操作
for(int i=1;i<tp;i++){
while(disl(h[i],h[i+1],h[t])<disl(h[i],h[i+1],h[t+1])) t=t%(tp-1)+1; //逆时针枚举点
ans=max(ans,max((h[i]-h[t]).dis(),(h[i+1]-h[t]).dis())); //两端点到此点
}
printf("%.0f",ans*ans);
return 0;
}
半平面クロス
ここでは、左または右半平面に半平面のベクトル表現を使用します
フロントチーズ:ラインを通過
同様の結果を得、それを描画
\ [H_1 = \ FRAC {ファブ ((V_2-P_1)*(P_2-P_1))} {DIS(V_2-P_2)}、H_2 = FRAC \ {ファブ((V_2-V_1) *(P_2-V_1))} {DIS(V_2-P_2)} \]
\ [線(P_1、V_1)\キャップライン(P_2、V_2)= P_1 +(V_1-P_1)* \ FRAC {H_1} {H_1 + H_2} \]
S&Iアルゴリズム
ソート極角
だから、\(\シータ=アークタンジェントFRAC YX \ \)と、その小さなベクトルの新しいセットを取得するために大規模なために
アルゴリズムのプロセス
反時計回り方向が正である、建物側(ライン)
セグメント極角発注
除去同じ極角の場合には、の位置右側面(左半平面であれば必要なポスト(反時計凸多角形)の単語)
両端キュー・ストレージ・セグメントの集合\(Lの\) 、すべてのセグメントを横断
- 解析線分が整然と追加されるので、線分の半平面の効果は、両端キュー判定の後、(頭部および尾部の後に添加し、影響を反対側のこの直線の交点を意味する(望ましくない副交差点前))、すなわち、副線路の右側か
当時最初の尾根を削除する必要があり、特定のwjyyy兄のブログを参照してください、後付けされます削除
(尾のヘッドが再び更新され、再び尾根を更新)影響最後に形成されたループを分析
- 最後に残りのセグメントの収集\(Lの\) 、最終の半平面の交差が必要としても
それは外積方向文全体で使用することができます
テンプレート
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define db double
const double eps=1e-9;
using namespace std;
const int N = 10001;
struct point{
double x,y;
point(double x=0,double y=0): x(x),y(y){}
double operator*(point b){ //叉积
return x*b.y-y*b.x;
}
double operator^(point b){ //点积
return x*b.x+y*b.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);
}
point operator*(double b){ //数乘
return point(x*b,y*b);
}
db dis(){ //模长
return sqrt(x*x+y*y);
}
}b[N];
int comp0(double x){
return fabs(x)<=eps?0:(x>0?1:-1);
}
struct line{
point p,v;//p起点,v终点 ,表示线段
double theta;
bool operator <(line y){
return comp0(theta-y.theta)==0?comp0((y.v-p)*(v-p))<0:comp0(theta-y.theta)<0;
}//排序,保证第一关键字是极角,第二关键字是与右边的距离(用叉积判相对关系,在左边的放后面)
};
point inter(line a,line b){//交点 intersection
point v1=a.v-a.p,v2=b.v-b.p;
return b.p+v2*(((b.p-a.p)*v1)/(v1*v2));
}//此处默认了没有平行的情况 , 判断线段有没有交点就是判断点是否在线段上,但是半平面交是直线(只是用线段表示)
int out(line a,line b,line k){
point ins=inter(a,b); //此处要判断的是ins是否在k的右边,画个图
return comp0((k.v-k.p)*(ins-k.p))<0;
}
int n;
line a[N],q[N];int top;
void work(){
sort(a+1,a+top+1);
int cnt=0;
for(int i=1;i<=top;i++){
if(comp0(a[i].theta-a[i-1].theta)!=0) cnt++;
a[cnt]=a[i];//极角相同时,右边的更优(排序保证了在左边)
}
int h=1,t=0;
q[++t]=a[1],q[++t]=a[2];//既然是交,至少包含两个元素
for(int i=3;i<=cnt;i++){
while(h<t&&out(q[t-1],q[t],a[i])) t--; //踢掉一些点 ,注意一定要先踢后面,不然会有些错
while(h<t&&out(q[h+1],q[h],a[i])) h++;
q[++t]=a[i];
}
while(h<t&&out(q[t-1],q[t],q[h])) t--; //由于是环形,判断影响
while(h<t&&out(q[h+1],q[h],q[t])) h++;
q[t+1]=q[h];
top=0;
for(int i=h;i<=t;i++){
b[++top]=inter(q[i],q[i+1]);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int k;scanf("%d",&k);
for(int j=1;j<=k;j++){
scanf("%lf%lf",&b[j].x,&b[j].y);
}
b[k+1]=b[1];
for(int j=1;j<=k;j++){
a[++top].p=b[j];a[top].v=b[j+1];//逆时针给出,如果不知道的话可以叉积判断
}
}
for(int i=1;i<=top;i++){
a[i].theta=atan2(a[i].v.y-a[i].p.y,a[i].v.x-a[i].p.x);
}
work();
double ans=0;
if(top<=2){
printf("%.3lf",0.0);return 0;//二 边 形
}
b[top+1]=b[1]; //同样,环形处理方法
for(int i=1;i<=top;i++){
ans+=(b[i]*b[i+1]);
}
ans/=2.0;
if(comp0(ans)==0) printf("%.3lf",0.0);
else{
printf("%.3lf",fabs(ans));
}
return 0;
}
最小ラウンドカバレッジ
ランダムな増分方法
ランダムな増分方法
インペリアル考えてみましょう\(I、Jの\)円で定数を、\(J <Iは、\)
以下のための\(\ FORALL K <J \)に到達するように構成され、\(I、J、Kの\ ) ラウンド
現在の場合\(K \)内円では、算出された\(K + 1 \)
そうでなければ更新\は(I、J、Kの\ ) (3点が円を決定する)円を外接しました
前の2については、円内のいくつかの点であれば、スキップ
それ以外の場合は、列挙\(J \ [1、i)は\) 、再計算します
時間複雑
QwQああ、円内のより多くのポイントは、それのどのくらいを最適化することはできません3サイクルのように見えますか?
しかし、私たちは始めている\(\ {テキストrandom_suffleを} \ ) の瞬間のために、そして希望感覚で複雑になって
だから、どのくらいですか?男性と女性のサイレント涙、それは驚異的に到達(\ O(N))\を!
分析:
- オーバーのための\(P_I、P_j \)円の少なくとも1回のサイクルの各点について\(O(J)\)
- オーバーのために\(P_I \)円、フロントカバー考える\を(私は\)円点は、3つの点によって決定され、その後、前\(I-1 \)点は、計算された2つの点があることが望ましいです次のレベル
- すべてのポイントをカバーする最小円の場合、3ポイントを貢献することが期待されています
- 总复杂度:\(O \左(\ sum_ {i = 1} ^ {N} \ FRAC {3} {I} \ sum_ {J = 1} ^ {I} \ FRAC {2} {J} \ CDOT J \右)= O(6 \ CDOTのN)= O(N)\)
テンプレート
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define db double
#include<cstdlib>
#include<ctime>
const double eps=1e-17;
using namespace std;
const int N = 100001;
struct point{
double x,y;
point(double x=0,double y=0): x(x),y(y){}
double operator*(point b){ //叉积
return x*b.y-y*b.x;
}
double operator^(point b){ //点积
return x*b.x+y*b.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);
}
point operator*(double b){ //数乘
return point(x*b,y*b);
}
db dis(){ //模长
return sqrt(x*x+y*y);
}
}p[N];
int comp0(double x){
return fabs(x)<=eps?0:(x>0?1:-1);
}
struct line{
point p,v;//p起点,v终点 ,表示线段
line(point a,point b): p(a),v(b){}
double theta;
bool operator <(line y){
return comp0(theta-y.theta)==0?comp0((y.v-p)*(v-p))<0:comp0(theta-y.theta)<0;
}//排序,保证第一关键字是极角,第二关键字是与右边的距离(用叉积判相对关系,在左边的放后面)
};
point inter(line a,line b){//交点 intersection
point v1=a.v-a.p,v2=b.v-b.p;
return b.p+v2*(((b.p-a.p)*v1)/(v1*v2));
}//此处默认了没有平行的情况 , 判断线段有没有交点就是判断点是否在线段上,但是半平面交是直线(只是用线段表示)
int out(line a,line b,line k){
point ins=inter(a,b); //此处要判断的是ins是否在k的右边,画个图
return comp0((k.v-k.p)*(ins-k.p))<0;
}
int n;
int in(point k,point c,double r){
return comp0(r-(k-c).dis())>=0;
}
point chrt(point a){
return point(-a.y,a.x);
}
void randomShuffle(){
srand(19260817+time(0)); //知道为什么不用STL的吗?
for (int i=1;i<=n;i++){
int j=(rand()%n+1926)%n+1;//这里随便rand()%n就行了,但我们要把玄学发扬光大【滑稽】
swap(p[i],p[j]);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf",&p[i].x,&p[i].y);
}
randomShuffle(); //这句很重要
point c;
db r=0.0;
for(int i=1;i<=n;i++){
if(in(p[i],c,r)) continue;
c=p[i],r=0;//重新计算
for(int j=1;j<i;++j){
if(in(p[j],c,r)) continue;
r=(p[j]-p[i]).dis()/2.0;
c=p[i]*0.5+p[j]*0.5;
for(int k=1;k<j;k++){
if(in(p[k],c,r)) continue;
line a=line((p[i]+p[j])*0.5,chrt(p[i]-p[j])+(p[i]+p[j])*0.5);
line b=line((p[k]+p[j])*0.5,chrt(p[j]-p[k])+(p[j]+p[k])*0.5);
c=inter(a,b);r=(c-p[i]).dis();
}
}
}
printf("%.10f\n%.10f %.10f",r,c.x,c.y);
return 0;
}
追伸
それはすべきであるにもval.3(学习nekopara
定積分ベース/ミンコフスキーおよび/シンプソンの統合(アダプティブ・シンプソン)ハンについて書くために、