Drawing Simple Polygon(Create Simple Polygon from unordered points by angle sorting)

Keywords: 极角排序, Simple Polygon Generation

Given set of points in the plane, your task is to draw a polygon using the points. You have to use all the points. To be more specific, each point of the set has to be a vertex of the polygon, and the polygon must not have any other vertices. No two line segments of the polygon may have any point in common, except for the middle vertex of two consecutive line segments. For example, given the points on the left-hand side, a valid polygon is shown on the right-hand side:

题意:

​ 给了一组无序的平面点集,目标是构造出一个Simple Polygon, Simple Polygon相比Polygon的定义约束是要求Polygon无边自交(题目也有具体的描述)。

分析:

​ 任意点序列看作一个Polygon。原始序列满足Simple Polygon的自交约束的对应序列可看作原始序列的特定Comparator下的排序结果。此处意味着可能存在一种Comparator, 通过其可套用通用的序列排序算法得到目标结果。

​ 答案是对标Convex Hull问题的经典算法Graham Scan的presort子算法,也就是极角排序。极角排序是二维点集有序化的一个经典思路, 普通的axis-based sort可看作极点在无穷远处的一个特例, 同时Graham Scan后部分Scan可以直接处理任意的Simple Polygon或小修后处理axis-based sorted点序列。此时有某种直观指引: 通过Graham Scan的微修版presort可以解决Simple Polygon的构造问题。

​ 多点共线(且其中一个点为极点)是极角排序需要补充定义进行处理的特殊情况, Graham Scan可将多点共线的处理延迟至Scan的实现上。补充多点共线点的偏序定义为极径增序,特例为入度方向相邻的共线区间为极径减序。

Why Impossible? All the Points are in a same line.

Code:

#include <iostream>
#include <array>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

namespace cglib {
    template <class Type = int>
    struct Vec2{
        Type x, y;
        Vec2(){}
        Vec2(Type _x, Type _y): x(_x), y(_y){}
        bool operator < (const Vec2& rhs) {
            return y == rhs.y ?  x < rhs.x : y < rhs.y;
        }

        Vec2 operator - (const Vec2& rhs) const {
            return Vec2(rhs.x - x, rhs.y - y);
        }

        double length2() const {
            return x * x + y * y;
        }
    };
    using vec2i = Vec2<int>;

    template <class Type>
    int to_left_test(const Type p, const Type q, const Type s) {
        int x = _Area(p, q, s);
        return  x == 0 ? -1: x > 0;
    }

    template <class Type>
    int _Area(const Type& p, const Type& q, const Type& s) {
        return p.x * q.y - p.y * q.x +
               q.x * s.y - q.y * s.x +
               s.x * p.y - s.y * p.x;
    }

    bool graham_presort(std::vector<vec2i>& P, std::vector<int>& idx) {
        std::swap(idx[0], idx[std::min_element(P.begin(), P.end())-P.begin()]);

        bool is_same_line = true;
        std::sort(idx.begin()+1, idx.end(), [&](const int& lhs, const int& rhs)->bool{
            switch (to_left_test(P[idx[0]], P[lhs], P[rhs])) {
                case -1:
                    return (P[lhs]-P[idx[0]]).length2() < (P[rhs]-P[idx[0]]).length2();
                case 0:
                    is_same_line = false;
                    return false;
                case 1:
                    is_same_line = false;
                    return true;
            }
        });
        if(!is_same_line) {
            for(int i = idx.size()-2; i > 0; i--) {
                if(to_left_test(P[idx[0]], P[*idx.rbegin()], P[idx[i]]) == -1) continue;
                std::reverse(idx.begin()+i+1, idx.end());
                break;
            }
        }
        return !is_same_line;
    }
}


int main() {
    using namespace cglib;
    int T, N;

    std::cin >> T;
    for(int t = 0; t < T; t++) {
        std::cin >> N;
        std::vector<vec2i> P;
        vec2i p;
        for(int i = 0; i < N; i++) {
            std::cin >> p.x >> p.y;
            P.emplace_back(p);
        }
        std::vector<int> idx(P.size());
        for(int i = 0; i < idx.size(); i++) idx[i] = i;

        std::cout << "Case " << t+1 << ":" << std::endl;
        if( !graham_presort(P, idx) ) {
            std::cout << "Impossible\n";
        }
        else {
            for(int i = 0; i < idx.size()-1; i++)
                std::cout << idx[i] << ' ';
            std::cout << *idx.rbegin() << std::endl;
        }

    }
}

posted @ 2019-07-17 16:03  xiconxi  阅读(...)  评论(...编辑  收藏