1. 程式人生 > 其它 >cf598 C. Nearest vectors(極角排序、比較角的大小)

cf598 C. Nearest vectors(極角排序、比較角的大小)

題意:

給定n個起點都為0的向量,找出一對夾角最小的

輸入均為整數

思路:

這題如果用atan2做極角排序,要開long double。正解是全程不用浮點數。

用叉積做極角排序,注意要判象限。這裡定義角度範圍 \((-pi,pi]\),所以 y負半軸 要排在末尾。

角的大小比較:設向量 \(v,u\) 的夾角是 \(\theta\),想辦法旋轉兩個向量,使 \(u\) 與 x正半軸重合。那麼新的向量終點是 \((|v|\cos(\theta),|v|\sin(\theta))\)。可以用縱座標/橫座標來衡量它的大小,即 \(v,u\) 的叉積除以點積。注意我們要讓 \(v\)\(u\) 的上方,所以叉積要取絕對值

struct point {
    ll x, y; int id;
};
ll dot(point a, point b) {
    return a.x * b.x + a.y * b.y;
}
ll cross(point a, point b) {
    return a.x * b.y - a.y * b.x;
}
bool below(point a) { //(pi,pi],y負半軸是最大值
    return a.y < 0 || a.y == 0 && a.x > 0;
}
bool polarLess(point a, point b) {
    if(below(a) != below(b)) return below(a);
    return cross(a, b) > 0;
}
bool angleLess(point a1, point b1, point a2, point b2) {
    point t1 = {abs(cross(a1, b1)), dot(a1, b1)}; //絕對值
    point t2 = {abs(cross(a2, b2)), dot(a2, b2)};
    return cross(t1, t2) < 0;
}

const int N = 1e5 + 3;
int n;
point a[N];

signed main() {
    iofast;
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> a[i].x >> a[i].y, a[i].id = i;

    sort(a + 1, a + 1 + n, polarLess);

    int ans1 = n, ans2 = 1;
    for(int i = 1; i < n; i++)
        if(angleLess(a[i], a[i+1], a[ans1], a[ans2]))
            ans1 = i, ans2 = i+1;
    cout << a[ans1].id << ' ' << a[ans2].id;
}