處理幾何問題的演算法
具有方向性的數值
\(A = (1, 2), B = (3, 1), \overrightarrow{AB} = (2, -1)\)
向量有加法減法運算
減法
加法
向量的長度記作\(|\vec v|\)
\(\vec a 和 \vec b的內積記作\vec a \cdot \vec b\)
\(\vec a\cdot\vec b = |\vec a||\vec b|\cos\theta\)
\(\vec a = (x_a, y_a)\)
\(\vec b = (x_b, y_b)\)
\(\vec a = (x_a, y_a)\)
\(\vec b = (x_b, y_b)\)
\(\vec a \cdot \vec b = x_a x_b + y_a y_b\)
用於判斷兩向量是否垂直
\(\vec a \cdot \vec b = |\vec a||\vec b|\cos\theta\)
如果\(\theta = 90\degree\)
\(cos \theta = 0\)
\(\vec a \cdot \vec b = 0\)
\(\begin{vmatrix}x_a & x_b \\y_a & y_b\end{vmatrix} = |\vec a||\vec b|\sin\theta\)
這邊用行列式的方式來表示
計算方式
\(\begin{vmatrix}x_a & x_b \\y_a & y_b\end{vmatrix}\)
\(\theta是\vec a 逆時針轉動到\vec b的夾角\)
\(\begin{vmatrix}x_a & x_b \\y_a & y_b\end{vmatrix} = |\vec a||\vec b|\sin\theta\)
這邊用行列式的方式來表示
計算方式
\(\begin{vmatrix}x_a & x_b \\y_a & y_b\end{vmatrix}=x_a y_b - x_b y_a\)
\(\theta是\vec a 逆時針轉動到\vec b的夾角\)
逆時針是正,順時針是負
\(\begin{Vmatrix}x_a & x_b \\ y_a & y_b \end{Vmatrix}是\vec a和\vec b所形成的平行四邊形的面積\)
\(|\vec a|\sin\theta\)
對於一個有n個點的多邊形
....
直接算兩直線交點
可以用參數式
有一直線\(\overleftrightarrow{AB}\)
\(\overrightarrow{AB}=(x_B - x_A, y_B - y_A)\)
對於直線\(\overleftrightarrow{AB}\)上的任一點P
\(P = A + t\overrightarrow{AB}\)
\(x_P = x_A + t(x_B - x_A)\)
\(y_P = y_A + t(y_B - y_A)\)
如果\(\overline{AB}線段和\overline{CD}線段相交\)
\(則CD兩點要在\overline{AB}的左右兩側\)
\(則AB兩點也要在\overline{CD}的左右兩側\)
可以用外積判斷
可以包住所有點的最小凸多邊形
凸多邊形:內角小於\(180\degree\)
我們把凸包分成上凸包和下凸包
先把所有點照x座標排序
從左往右把點一個一個加進去
用一個stack來維護目前為止的凸包
A
B
每次加入時要先看最前面的兩個點
先把所有點照x座標排序
從左往右把點一個一個加進去
用一個stack來維護目前為止的凸包
B
每次加入時要先看最前面的兩個點
C
A
先把所有點照x座標排序
從左往右把點一個一個加進去
用一個stack來維護目前為止的凸包
B
每次加入時要先看最前面的兩個點
C
A
先把所有點照x座標排序
從左往右把點一個一個加進去
用一個stack來維護目前為止的凸包
每次加入時要先看最前面的兩個點
C
A
用外積判斷旋轉方向
C
A
B
可以看\(\overrightarrow{AB}和\overrightarrow{BC}的旋轉方向來判斷\)
下凸包就把排序好的點反過來再做一次
但要小心處理左右端點
下凸包就把排序好的點反過來再做一次
要小心處理左右端點
如果一邊有兩個端點
要讓上面的在上凸包
下面的在下凸包
如果只有一個端點
讓左端點在上凸包
右端點
要怎麼做到?
可以在排序時,把x座標小的放前面
如果x座標相同,把y座標大的放前面
bool cmp(pii x, pii y){
if(x.first != y.first){
return x.first < y.first;
}else{
return x.second > y.second;
}
}sort(point, point + n, cmp);
vector<pii> upconvex, downconvex;
for(int j = 0; j < n; j++){
while(upconvex.size() > 1 && cross(upconvex.back() - upconvex[upconvex.size() - 2], point[j] - upconvex.back()) >= 0){
upconvex.pop_back();
}
upconvex.push_back(point[j]);
}
upconvex.pop_back();
reverse(point, point + n);
for(int j = 0; j < n; j++){
while(downconvex.size() > 1 && cross(downconvex.back() - downconvex[downconvex.size() - 2], point[j] - downconvex.back()) >= 0){
downconvex.pop_back();
}
downconvex.push_back(point[j]);
}
downconvex.pop_back();sort(point, point + n, cmp);
vector<pii> convex;
int m = 0;
for(int i = 0; i < 2; i++){
for(int j = 0; j < n; j++){
while(convex.size() - m > 1 && cross(convex.back() - convex[convex.size() - 2], point[j] - convex.back()) >= 0){
convex.pop_back();
}
convex.push_back(point[j]);
}
convex.pop_back();
m = convex.size();
reverse(point, point + n);
}可以用同一個stack做上下凸包
照角度排序
用一個點當作中心,把其他點照順序排成一圈
atan2(y, x)
他會回傳從x軸正向逆時針旋轉到點(x, y)的角度\(\theta\)
在y小於0時,\(\theta\)是負的
typedef pair<int, int> pii;
bool cmp(pii a, pii b){
return atan2(a.first, a.second) < atan2(b.first, b.second);
}外積
用外積判斷旋轉方向
記得先判y的正負
bool cross(pii a, pii b){
return a.first * b.second - a.second * b.first;
}
bool cmp(pii a, pii b){
bool c = (a.second > 0 || (a.second == 0 && a.first > 0));
bool d = (b.second > 0 || (b.second == 0 && b.second > 0));
if(c != d)
return c > d;
return cross(a, b) < 0;
}