[旋转卡壳] HDU-6830 Asteroid in Love

题目链接

During the summer vacation, the geoscience community launched a constellation observation activity.

On a clear night, Mira and Ao set up their telescopes. Through the telescopes, they observed n stars. These n stars could be regarded as points on a two-dimensional plane, with coordinates (x_i,y_i) .

It was boring to observe the stars alone, so they classified these stars into three categories according to their spectrum: R-type stars, G-type stars, and B-type stars.

Now, Mira wants to form a constellation named after Ao, for Ao to make up for the regret that there is no star named after Ao. This constellation should be small and beautiful. She wants the number of stars to be the least in this constellation. Besides, she wants the constellation contains all types of stars. It is easy to know that this constellation has only three stars, and the types of these three stars are R type, G type, and B type.

She also wants the area of the convex polygon enclosed by the stars of this constellation is the largest, that is, the area of the triangle formed by these three stars is the largest. If the triangle is trivial (that is, the three points that make up the triangle are on the same straight line), the area of the triangle is considered to be 0 .

Please tell Mira the largest area of Constellation Ao.

Input

The first line contains an integer T , indicating the number of testcase.

For each testcase, the first line contains a positive integer n, indicating the number of stars observed.

From line 2 to line n+1, each line contains three integers x,y,c. Line i describes the information of the stars i−1, where x,y are the horizontal and vertical coordinates of the star's position, that is, the coordinates of the star are (x,y) ; c is for the description of stellar types, 0 indicates that the stars are B-type stars, 1 indicates R-type stars, and 2 indicates G-type stars.

T≤10,3≤n≤3×10^3,|x|,|y|≤10^9,0≤c≤2

It is guaranteed that there exists a Constellation Ao, and any two stars will not be in the same position.

Output

For each testcase, output one real number per line indicates the maximum area. The answer should be rounded to one decimal place.

Sample Input

2
8
3 1 0
-5 3 0
-1 1 1
-1 -1 0
-2 1 0
2 -4 0
1 1 0
7 7 2
17
-5 1 0
-4 1 0
-3 1 0
-2 1 0
-1 1 0
0 1 0
1 1 0
2 1 0
3 1 0
4 1 0
5 1 0
-2 3 1
-4 2 1
7 5 1
9 1 2
7 3 2
-1 3 2

Sample Output

29.0
28.0

思路

题目就是给出 3 个点集,现在要分别从这三个点集中选出一个点组成一个三角形,问三角形最大面积是多少。
最坏情况下每个点集只有 1000 个点,所以考虑暴力枚举两个点,然后通过一些手段去寻找第三个点。
可以考虑枚举前两个点形成的一条直线,可以发现对于这两个点,如果想要形成的三角形面积最大,第三个点只可能是被直线切开的两个半平面中距离直线最远的点。那么先只考虑直线左侧的点,可以将直线极角排序,像旋转卡壳那样进行旋转,同时维护距离最远的点进行答案的更新即可。

代码

#include <bits/stdc++.h>
using namespace std;
void IO()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}
typedef double db;
const db eps = 1e-9;
const db pi = acos(-1.0);
int sign(db k)
{
    if (k > eps)
        return 1;
    else if (k < -eps)
        return -1;
    return 0;
}
int cmp(db k1, db k2) { return sign(k1 - k2); }
int inmid(db k1, db k2, db k3) { return sign(k1 - k3) * sign(k2 - k3) <= 0; } // k3 在 [k1,k2] 内
struct point
{
    db x, y;
    point operator+(const point &k1) const { return (point){k1.x + x, k1.y + y}; }
    point operator-(const point &k1) const { return (point){x - k1.x, y - k1.y}; }
    point operator*(db k1) const { return (point){x * k1, y * k1}; }
    point operator/(db k1) const { return (point){x / k1, y / k1}; }
    point operator-() const { return (point){-x, -y}; }
    int operator==(const point &k1) const { return cmp(x, k1.x) == 0 && cmp(y, k1.y) == 0; }
    // 逆时针旋转
    point turn(db k1) { return (point){x * cos(k1) - y * sin(k1), x * sin(k1) + y * cos(k1)}; }
    point turn90() { return (point){-y, x}; }
    point turn270() { return (point){y, -x}; }
    bool operator<(const point k1) const
    {
        int a = cmp(x, k1.x);
        if (a == -1)
            return 1;
        else if (a == 1)
            return 0;
        else
            return cmp(y, k1.y) == -1;
    }
    db abs() { return sqrt(x * x + y * y); }
    db abs2() { return x * x + y * y; }
    db dis(point k1) { return ((*this) - k1).abs(); }
    point unit()
    {
        db w = abs();
        return (point){x / w, y / w};
    }
    friend istream &operator>>(istream &is, point &k)
    {
        is >> k.x >> k.y;
        return is;
    }
    friend ostream &operator<<(ostream &os, const point &k)
    {
        os << fixed << setprecision(2) << "(" << k.x << "," << k.y << ")\n";
        return os;
    }
    db getw() { return atan2(y, x); }
    point getdel()
    {
        if (sign(x) == -1 || (sign(x) == 0 && sign(y) == -1))
            return (*this) * (-1);
        else
            return (*this);
    }
    int getP() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) == -1); }
};
int inmid(point k1, point k2, point k3) { return inmid(k1.x, k2.x, k3.x) && inmid(k1.y, k2.y, k3.y); }
inline db cross(point k1, point k2) { return k1.x * k2.y - k1.y * k2.x; }
db dot(point k1, point k2) { return k1.x * k2.x + k1.y * k2.y; }
db rad(point k1, point k2) { return atan2(cross(k1, k2), dot(k1, k2)); }
// -pi -> pi
bool compareangle(const point &k1, const point &k2)
{
    return k1.getP() < k2.getP() || (k1.getP() == k2.getP() && sign(cross(k1, k2)) > 0);
}
point proj(point k1, point k2, point q)
{ // q 到直线 k1,k2 的投影
    point k = k2 - k1;
    return k1 + k * (dot(q - k1, k) / k.abs2());
}
point reflect(point k1, point k2, point q) { return proj(k1, k2, q) * 2 - q; }
int clockwise(point k1, point k2, point k3)
{ // k1 k2 k3 逆时针 1 顺时针 -1 否则 0
    return sign(cross(k2 - k1, k3 - k1));
}
int checkLL(point k1, point k2, point k3, point k4)
{ // 求直线 (L) 线段 (S)k1,k2 和 k3,k4 的交点
    return cmp(cross(k3 - k1, k4 - k1), cross(k3 - k2, k4 - k2)) != 0;
}
point getLL(point k1, point k2, point k3, point k4)
{
    db w1 = cross(k1 - k3, k4 - k3), w2 = cross(k4 - k3, k2 - k3);
    return (k1 * w2 + k2 * w1) / (w1 + w2);
}
int intersect(db l1, db r1, db l2, db r2)
{
    if (l1 > r1)
        swap(l1, r1);
    if (l2 > r2)
        swap(l2, r2);
    return cmp(r1, l2) != -1 && cmp(r2, l1) != -1;
}
int checkSS(point k1, point k2, point k3, point k4)
{
    return intersect(k1.x, k2.x, k3.x, k4.x) && intersect(k1.y, k2.y, k3.y, k4.y) &&
           sign(cross(k3 - k1, k4 - k1)) * sign(cross(k3 - k2, k4 - k2)) <= 0 &&
           sign(cross(k1 - k3, k2 - k3)) * sign(cross(k1 - k4, k2 - k4)) <= 0;
}
db disSP(point k1, point k2, point q)
{
    point k3 = proj(k1, k2, q);
    if (inmid(k1, k2, k3))
        return q.dis(k3);
    else
        return min(q.dis(k1), q.dis(k2));
}
db disSS(point k1, point k2, point k3, point k4)
{
    if (checkSS(k1, k2, k3, k4))
        return 0;
    else
        return min(min(disSP(k1, k2, k3), disSP(k1, k2, k4)), min(disSP(k3, k4, k1), disSP(k3, k4, k2)));
}
int onS(point k1, point k2, point q) //点q在点k1,k2之间
{
    return inmid(k1, k2, q) && sign(cross(k1 - q, k2 - k1)) == 0;
}
struct circle
{
    point o;
    db r;
    int inside(point k) { return cmp(r, o.dis(k)); }
};
struct line
{
    // p[0]->p[1]
    point p[2];
    line(point k1, point k2)
    {
        p[0] = k1;
        p[1] = k2;
    }
    point &operator[](int k) { return p[k]; }
    int include(point k) { return sign(cross(p[1] - p[0], k - p[0])) > 0; }
    point dir() { return p[1] - p[0]; }
    line push()
    { // 向外 ( 左手边 ) 平移 eps
        const db eps = 1e-6;
        point delta = (p[1] - p[0]).turn90().unit() * eps;
        return {p[0] - delta, p[1] - delta};
    }
};
point getLL(line k1, line k2) { return getLL(k1[0], k1[1], k2[0], k2[1]); }
int parallel(line k1, line k2) { return sign(cross(k1.dir(), k2.dir())) == 0; }
int sameDir(line k1, line k2) { return parallel(k1, k2) && sign(dot(k1.dir(), k2.dir())) == 1; }
int operator<(line k1, line k2)
{
    if (sameDir(k1, k2))
        return k2.include(k1[0]);
    return compareangle(k1.dir(), k2.dir());
}
vector<point> ConvexHull(vector<point> A, int flag = 1) //凸包
{                           // flag=0 不严格 flag=1 严格
    int n = A.size();
    vector<point> ans(n * 2);
    sort(A.begin(), A.end());
    int now = -1;
    for (int i = 0; i < A.size(); i++)
    {
        while (now > 0 && sign(cross(ans[now] - ans[now - 1], A[i] - ans[now - 1])) < flag)
            now--;
        ans[++now] = A[i];
    }
    int pre = now;
    for (int i = n - 2; i >= 0; i--)
    {
        while (now > pre && sign(cross(ans[now] - ans[now - 1], A[i] - ans[now - 1])) < flag)
            now--;
        ans[++now] = A[i];
    }
    ans.resize(now);
    return ans;
}

//-----------------------------
vector<vector<point>> vp(3);
vector<line> vl;
int n;

inline long double spcross(const point &k1, const point &k2)
{
    return (long double)k1.x * k2.y - (long double)k1.y * k2.x;
}

void solve()
{
    cin >> n;
    for (int i = 0; i < 3; i++)
        vp[i].clear();
    vl.clear();
    for (int i = 0, color; i < n; i++)
    {
        point tem;
        cin >> tem >> color;
        vp[color].push_back(tem);
    }
    sort(vp.begin(), vp.end(), [](const vector<point> &k1, const vector<point> &k2) {
        return k1.size() < k2.size();
    });

    if (vp[2].size() > 2)
        vp[2] = ConvexHull(vp[2]);

    for (int i = 0; i < vp[0].size(); i++)
        for (int j = 0; j < vp[1].size(); j++)
            vl.push_back({vp[0][i], vp[1][j]});
    sort(vl.begin(), vl.end());

    int i = 0, j = 0, sz = vp[2].size();
    db ansi = -1e21, ansj = 1e21;
    long double ans = -1;
    point p1 = vl[0][0], p2 = vl[0][1];
    for (int id = 0; id < vp[2].size(); id++)
    {
        db tem = (cross(p2 - p1, vp[2][id] - p1));
        if (cmp(tem, ansi) > 0)
        {
            ansi = tem;
            i = id;
        }
    }
    for (int id = 0; id < vp[2].size(); id++)
    {
        db tem = (cross(p2 - p1, vp[2][id] - p1));
        if (cmp(tem, ansj) < 0)
        {
            ansj = tem;
            j = id;
        }
    }
    for (auto &x : vl)
    {
        point p1 = x[0], p2 = x[1];

        while (cmp((cross(p2 - p1, vp[2][i] - p1)), cross(p2 - p1, (vp[2][(i + 1) % sz] - p1))) < 0)
            i = (i + 1) % sz;
        while (cmp((cross(p2 - p1, vp[2][j] - p1)), cross(p2 - p1, (vp[2][(j + 1) % sz] - p1))) > 0)
            j = (j + 1) % sz;
        ans = max({ans, fabs(spcross(p2 - p1, vp[2][i] - p1)), fabs(spcross(p2 - p1, vp[2][j] - p1))});
    }
    cout << fixed << setprecision(1) << ans / 2 << '\n';
}

int main()
{
    IO();
    int _;
    cin >> _;
    while (_--)
        solve();
}

吐槽

由于答案很大,double 可能会爆精度,比赛的时候就因为这个 wa 的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354