【BZOJ3707】圈地 (几何,旋转坐标系)

【BZOJ3707】圈地 (几何,旋转坐标系)

Description

2维平面上有n个木桩,黄学长有一次圈地的机会并得到圈到的土地,为了体现他的高风亮节,他要使他圈到的土地面积尽量小。圈地需要圈一个至少3个点的多边形,多边形的顶点就是一个木桩,圈得的土地就是这个多边形内部的土地。(因为黄学长非常的神,所以他允许圈出的第n点共线,那样面积算0)

Input

第一行一个整数n,表示木桩个数。
接下来n行,每行2个整数表示一个木桩的坐标,坐标两两不同。

Output

仅一行,表示最小圈得的土地面积,保留2位小数。

Sample Input

3
0 0
0 1
1 0

Sample Output

0.50

题解:

如果我们确定了2个点以后,第三个点有必要去盲目的枚举吗?答案是否定的。实际上我们把经过这两点的线看成一个斜率,把他当成y轴你会发现第三个点明显是在坐标系左右找一个离”y轴”最近的点来算面积更新答案。然后我们可以继续思考,发现我们可以把点按照某个斜率当成”y轴”进行“从左到右”的排序,这样当2点共线的时候,用这两个点的左右2个点去更新答案就好了。也就是说我们采用旋转坐标系的方法,一开始按x坐标排好序,认为直接用竖着的那条斜率,然后维护的话每次其实当两点共线后只要交换他们就能得到斜率转过该事件点的序列。所以我们可以预处理出所有可行的斜率,当成事件点,不断转动坐标系更新答案就好。这样复杂度只有n^2。

代码:

int n;
struct point {
    ll x, y;
    point() {}
    point(int xx, int yy)
    {
        x = xx; y = yy;
    }
    bool operator < (const point &b)const
    {
        return y < b.y;
    }
    point operator - (const point &b) const
    {
        return point(x - b.x, y - b.y);
    }
    ll operator ^ (const point &b) const
    {
        return x * b.y - b.x * y;
    }
} a[1010];
#define eps 1e-6
struct line {
    int xid, yid;
    double polar;
    line() {}
    line(int xx, int yy)
    {
        xid = xx; yid = yy;
        polar = atan2(a[yy].y - a[xx].y, a[yy].x - a[xx].x);
    }
    bool operator < (const line &b) const
    {
        return polar < b.polar;
    }
};
std::vector<line> v;
ll ans = 1e18;
ll cal(int i, int j, int k)
{
    return abs((a[i] - a[j]) ^ (a[i] - a[k]));
}
int id[maxn];
int info[maxn];
int main()
{
#if DEBUG_Switch
    freopen("D:\\code\\input.txt", "r", stdin);
#endif
    //freopen("D:\\code\\output.txt","w",stdout);
    n = readint();
    repd(i, 1, n) {
        a[i].x = readint();
        a[i].y = readint();
    }
    sort(a + 1, a + 1 + n);
    repd(i, 1, n) {
        repd(j, i + 1, n) {
            v.push_back(line(i, j));
        }
        id[i] = info[i] = i;
    }
    sort(ALL(v));
    for (auto l : v) {
        int t1 = l.xid;
        int t2 = l.yid;
//        cout<<t1<<" "<<t2<<endl;
        if (id[t1] > id[t2]) {
            swap(t1, t2);
        }
        if (id[t1] > 1) {
            ans = min(cal(info[id[t1] - 1], t1, t2), ans);
        }
        if (id[t2] < n) {
            ans = min(cal(info[id[t2] + 1], t1, t2), ans);
        }
        swap(id[t1], id[t2]);
        info[id[t1]]=t1;
        info[id[t2]]=t2;
    }
    printf("%.2f\n", 0.5 * ans );
    return 0;
}

posted @ 2020-10-22 16:21  茄子Min  阅读(156)  评论(0编辑  收藏  举报