高雄中學 洪駿輝
codingbeans
struct Point
{
double x,y;
};
struct Vector
{
double x,y;
};
struct Point
{
double x,y;
};
typedef Point Vector;
struct Point
{
double x,y;
Point(double x=0,double y=0):x(x),y(y) {}
};
typedef Point Vector;
// Vector + Vector = Vector
Vector operator + (Vector A,Vector B) {return Vector(A.x+B.x,A.y+B.y);}
// Point - Point = Vector
Vector operator - (Point A,Point B) {return Vector(A.x-B.x,A.y-B.y);}
// Vector * num = Vector
Vector operator * (Vector A,double t) {return Vector(A.x*t,A.y*t);}
// Vector / num = Vector
Vector operator / (Vector A,double t) {return Vector(A.x/t,A.y/t);}
double Dot(Vector A,Vector B) {return A.x * B.x + A.y * B.y;}
double Length(Vector A) {return sqrt(Dot(A,A));} // sqrt(A.x*A.x + A.y*A.y)
double getangle(Vector A,Vector B) {return acos( Dot(A,B)/Length(A)/Length(B) );}
一個同時垂直於a,b的向量,顯然平行z軸
可是這是二維幾何耶T_T
double Cross(Vector A,Vector B) {return A.x*B.y - A.y*B.x;}
double Area(Point A,Point B,Point C) {return Cross(B-A,C-A) / 2.0;}
注意:有向面積
兩點式
點和向量決定唯一直線
struct Line
{
Point p;
Vector v;
double ang;
Line(Point p,Vector v):p(p),v(v) {ang=atan2(v.y,v.x);}
};
code省略 :)
typedef vector<Point> Polygon;
struct Circle
{
Point c;
double r;
Circle(Point c,double r=0):c(c),r(r) {}
};
測量師公式:
都很煩
思路:不外乎
1.硬解x,y聯立方程
2.硬解參數t
3.算長度,算圓上極角,求得點
從給定點往右引一條無限遠的射線,若與多邊形相交奇數次則在多邊形內部,偶數次則否。
多邊形相對於給定點轉了幾度
內部 , 外部 , 邊上
bool isPointOnSegment(Point P,Point A,Point B)
{
return Cross(P-A,P-B) == 0 && Dot(P-A,P-B) <= 0;
}
typedef vector<Point> Polygon;
int IsPointInPolygon(Point p,Polygon poly)
{
int n= poly.size();
int wn=0;
for(int i=0;i<n;i++)
{
if(isPointOnSegment(p,poly[i],poly[(i+1)%n])) return -1; // 邊上
int k = Cross( poly[(i+1)%n] - poly[i] , p - poly[i]);
int d1 = poly[i].y - p.y;
int d2 = poly[(i+1)%n].y - p.y;
if(k > 0 && d1<=0 && d2>0) wn++; //逆時針穿過,繞數+1
if(k < 0 && d1>0 && d2<=0) wn--; //順時針穿過,繞數-1
}
return wn!=0;
}
C
P1
P2
枚舉 條線段,每條線段花 判斷是否在凸包上(即是否所有點都在此線段同一側),時間複雜度
1. 找到y值最小的點,若有多個,選擇x值最小的點。設此點為P
2. 將所有點依照對P的極角排序。
3. 依照極角序加入點。
構成左轉時,直接加入點至末端。
當發現需要右轉時,不斷彈出末端的
點直到構成左轉。
1. 將所有點按照x的座標大小排序,若x相同則依y的大小排序。
2. 和Graham算法類似。依照順序加入點,若構成右轉則不斷彈
出末端的點直到形成左轉。
3. 第2步完畢時,已有下凸包。將下凸包最右的點作為起點,反
向進行步驟2,即可得到上凸包。合併即是所求凸包。
struct Point
{
double x,y;
Point(double x=0,double y=0):x(x),y(y) {}
bool operator < (const Point& rhs) const {return x<rhs.x || (x==rhs.x && y<rhs.y);}
};
// Andrew
// 回傳凸包頂點數量
int ConvexHull(Point* p,int n,Point* ch)
{
sort(p,p+n);
int m=0;
// 下凸包
for(int i=0;i<n;i++)
{
while(m > 1 && Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2]) <=0 ) m--;
ch[m++]=p[i];
}
int k=m;
// 上凸包
for(int i=n-2;i>=0;i--)
{
while(m > k && Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2]) <=0 ) m--;
ch[m++]=p[i];
}
// 注意p[0]會重複選到
if(n>1) m--;
return m;
}
1. 任一時間最多只有4對對踵點對
2. 只需考慮一條卡尺貼邊的情況,計算出另一條卡尺的位置並更新答案即可。
double Rotating_Calipers(Point* p,int n)
{
if(n<=1) return 0;
if(n==2) return dist(p[0],p[1]);
double ans=0;
for(int i=0,j=2;i<n;i++)
{
// 若穿過p[j+1]比穿過p[j]要離主卡尺更遠則j<-j+1,實作上直接用外積判斷
while(Cross(p[(i+1)%n]-p[i],p[(j+1)%n]-p[i])
> Cross(p[(i+1)%n]-p[i],p[j]-p[i])) j=(j+1)%n;
ans = max(ans,dist(p[i],p[j]));
ans = max(ans,dist(p[(i+1)%n],p[j]));
}
return ans;
}
例題:給一個n<=100000個點的凸多邊形,求包住此凸多邊形的最小矩形面積
想法不難,可以想想看:)
定義有向直線的左邊就是他所對應的半平面
半平面交指的就是這些半平面的交集
struct Line
{
Point p;
Vector v;
double ang;
Line(Point p=0,Vector v=0):p(p),v(v) {ang=atan2(v.y,v.x);}
bool operator < (const Line& rhs) const {return ang < rhs.ang;}
};
// P 是否在有向直線 L 的左邊
inline bool OnLeft(Point P,Line L)
{
return Cross(L.v,P-L.p) > 0;
}
// 回傳兩直線交點
Point GetLineIntersec(Line L1,Line L2)
{
double t = Cross(L2.p-L1.p,L2.v) / Cross(L1.v,L2.v);
return L1.p + L1.v*t;
}
// 回傳凸多邊形點數
int HalfPlaneIntersec(Line* L,int n,Point* poly)
{
sort(L,L+n);
int first=0,last=0; // deque 的指針
Point* p = new Point[n];
Line* q = new Line[n];
q[last++]=L[0];
for(int i=1;i<n;i++)
{
while(last-first>=2 && !OnLeft(p[last-2],L[i])) last--;
while(last-first>=2 && !OnLeft(p[first],L[i])) first++;
if(Cross(L[i].v,q[last-1].v)==0) // 有向直線平行,取內側的直線
{
if(OnLeft(L[i].p,q[last-1])) q[last]=L[i];
}
else q[last++]=L[i];
if(last-first>=2) p[last-2] = GetLineIntersec(q[last-1],q[last-2]);
}
while(last-first>=2 && !OnLeft(p[last-2],q[first])) last--;
if(last-first<3) return 0; // 無界多邊形
p[last-1]=GetLineIntersec(q[first],q[last-1]);
int m=0;
for(int i=first;i<last;i++) poly[m++]=p[i];
delete[] p;delete[] q;
return m;
}
1. 進: 掃描線通過矩形左界
2. 出: 掃描線通過矩形右界
1. 線段左端點(進)
2. 線段右端點(出)
題目敘述:
有n<=100000個運動員參加運動比賽,這場比賽有兩個項目:游泳R公尺和賽跑S公尺。選手在完成一項後會立即開始下一項
你知道這n個運動員各自在這兩個項目的速度:第i個人每秒游r_i公尺,跑s_i公尺。但是你不知道R,S分別是多少,只知道他們是正實數。
斜率為負的這條直線在點集當中能取到的最小值發生在凸包的左下方
目前沒有人知道QAQ
雖然沒有好用的順序,還是可以把點一個一個加進來然後維護好當前凸包。
誤差分析 -- eps
三分搜尋法,爬山法,梯度下降法,模擬退火法
數值方法:
隨機增量法: 最小包含圓問題
結合圖論算法 e.g. PSLG
幾何只是輔助,其實要考(資結/數論)
複雜的數學分析與建模
半平面交算法及简单应用
http://www.cnblogs.com/huangxf/p/4067763.html
https://en.wikipedia.org/wiki/Graham_scan
旋转卡壳算法 - ACM算法
http://www.alphaway.org/post-93.html