[计算几何]极角排序

本文介绍了极角排序在处理几何问题中的重要作用,包括使用atan2和叉积两种方法,并展示了如何通过极角排序解决点集的最大不相交子集问题。代码示例中详细阐述了极角排序的实现细节,如如何处理精度误差、如何进行整数运算,以及如何利用尺取法模拟扫描线。此外,还讨论了在不同场景下如何将黑点转化为对称的白点以简化计算。
摘要由CSDN通过智能技术生成

Amphiphilic Carbon Molecules
http://fastvj.rainng.com/contest/304637#problem/A
保存个极角排序的板子。
极角排序有两种。一种是atan2,速度快,但是有精度误差。另一种是叉积,速度慢但是只用进行整数运算。
注意用叉积极角排序的时候,要把上半区和下半区分开来排,而且每个点所在的半区要唯一。

struct point{
    int x, y, c;
    double rad;//atan2(y, x)
    bool operator < (const point &t) const {return rad < t.rad;}
    int getP() {return y > 0 || (y == 0 && x >= 0);}
}a[maxn];
int cross(point a, point b) {return a.x * b.y - a.y * b.x;}
bool cmp(point a, point b)
{
    if (a.getP() == b.getP()) return cross(a, b) > 0;
    else return a.getP() > b.getP();
}

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 5, inf = 0x3f3f3f3f;
struct point{
    int x, y, c;
    double rad;//atan2(y, x)
    bool operator < (const point &t) const {return rad < t.rad;}
    int getP() {return y > 0 || (y == 0 && x >= 0);}
}a[maxn];
int cross(point a, point b) {return a.x * b.y - a.y * b.x;}
bool cmp(point a, point b)
{
    if (a.getP() == b.getP()) return cross(a, b) > 0;
    else return a.getP() > b.getP();
}
vector<point> vp;
int main()
{
    //ios::sync_with_stdio(0); cin.tie(0);
    int n;
    while (cin >> n){
        if (n == 0) break;
        if (n == 1 || n == 2){
            cout << 2 << '\n';
            continue;
        }
        for (int i = 0; i < n; ++i) cin >> a[i].x >> a[i].y >> a[i].c;
        int ans = 0;
        for (int i = 0; i < n; ++i){
            //枚举点,极角排序
            for (int j = 0; j < n; ++j){
                if (i == j) continue;
                //神级技巧啊,把黑点转化为对称的白点,这样就只用统计数量就可以了
                if (a[j].c) vp.push_back(point{-(a[j].x - a[i].x), -(a[j].y - a[i].y), a[j].c, atan2(-(a[j].y - a[i].y), -(a[j].x - a[i].x))});
                else vp.push_back(point{a[j].x - a[i].x, a[j].y - a[i].y, a[j].c, atan2(a[j].y - a[i].y, a[j].x - a[i].x)});
            }
            sort(vp.begin(), vp.end(), cmp);
            //神级技巧2,尺取法模拟扫描线
            //起手是2是因为枚举了一个点,然后从第一个点开始扫
            int l = 0, r = 0, cnt = 2, sz = vp.size();
            while (l < sz){
                if (r == l) ++cnt, r = (r + 1) % sz;
                while (r != l && cross(vp[l], vp[r]) >= 0) ++cnt, r = (r + 1) % sz;
                ++l, --cnt;
                ans = max(ans, cnt);
            }
            vp.clear();
        }
        cout << ans << '\n';
    }
    return 0;
}

CODECHEF – Red and blue points

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 5, inf = 2e9 + 10;
struct point{
    //注意爆int
    ll x, y, c;
    double rad;//atan2(y, x)
    bool operator < (const point &t) const {return rad < t.rad;}
    ll getP() {return y > 0 || (y == 0 && x >= 0);}
}a[maxn];
ll cross(point a, point b) {return a.x * b.y - a.y * b.x;}
bool cmp(point a, point b)
{
    if (a.getP() == b.getP()) return cross(a, b) > 0;
    else return a.getP() > b.getP();
}
vector<point> vp;
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    int t;
    cin >> t;
    while (t--){
        int n, m;
        cin >> n >> m;
        for (int i = 0; i < n; ++i){
            cin >> a[i].x >> a[i].y;
            a[i].c = 0;
        }
        for (int i = n; i < n + m; ++i){
            cin >> a[i].x >> a[i].y;
            a[i].c = 1;
        }
        int ans = inf;
        for (int i = 0; i < n + m; ++i){
            for (int j = 0; j < n + m; ++j){
                if (i == j) continue;
                vp.push_back(point{a[j].x - a[i].x, a[j].y - a[i].y, a[j].c, atan2(a[j].y - a[i].y, a[j].x - a[i].x)});
            }
            sort(vp.begin(), vp.end(), cmp);
            int l = 0, r = 0, g0 = 0, g1 = 0, c0 = 0, c1 = 0, sz = vp.size();
            if (a[i].c == 0) g0 = 1;
            else g1 = 1;
            while (l < sz){
                if (r == l){
                    if (vp[r].c == 0) ++g0;
                    else ++g1;
                    r = (r + 1) % sz;
                }
                while (r != l && cross(vp[l], vp[r]) > 0){
                        if (vp[r].c == 0) ++c0;
                        else ++c1;
                        r = (r + 1) % sz;
                }
                ans = min(ans, c0 + m - c1 - g1);
                ans = min(ans, c1 + n - c0 - g0);
                if (vp[l].c == 0) --g0;
                else --g1;
                ++l;
                //l != r的时候才要清,否则r就少算一次
                if (l != r){
                    if (vp[l].c == 0) --c0, ++g0;
                    else --c1, ++g1;
                }
            }
            vp.clear();
        }
        cout << ans << endl;
    }
    return 0;
}

又有极角排序了。
https://cn.vjudge.net/contest/319435#problem/J

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1600;
struct point{
    ll x, y;
    int getP() {return y > 0 || (y == 0 && x >= 0);}
}a[maxn], ta[maxn];
ll cross(point a, point b) {return a.x * b.y - a.y * b.x;}
bool cmp(point a, point b)
{
    if (a.getP() == b.getP()) return cross(a, b) > 0;
    else return a.getP() > b.getP();
}
ll dis2(point a, point b)
{
    return (a.x - b.x) * (a.x - b.x)  + (a.y - b.y) * (a.y - b.y);
}
ll yu(point a, point b, point c)
{
    ll da = dis2(a, b), db = dis2(a, c), dc = dis2(b, c);
    return da + db - dc;
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i].x >> a[i].y;
    ll ans = 0;
    for (int i = 1; i <= n; ++i){
        int p = 0;
        set<pii> sp;
        for (int j = 1; j <= n; ++j){
            if (i == j) continue;
            ta[p].x = a[j].x - a[i].x;
            ta[p].y = a[j].y - a[i].y;
            sp.insert(pii(ta[p].x, ta[p].y));
            ++p;
        }
        sort(ta, ta + p, cmp);
        int l = 0, r = 0;
        point zx = point{0, 0};
        while (l < p){
            if (r == l) r = (r + 1) % p;
            while (r != l && yu(zx, ta[l], ta[r]) > 0) r = (r + 1) % p;
            while (r != l && yu(zx, ta[l], ta[r]) == 0){
                if (sp.count(pii(ta[l].x + ta[r].x, ta[l].y + ta[r].y)))
                    ans = max(ans, (ll)(sqrt(dis2(zx, ta[l])) * sqrt(dis2(zx, ta[r])) + 0.5));
                r = (r + 1) % p;
            }
            ++l;
        }
    }
    cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值