扫描线算法判断多边形是否合法

题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1010

题目大意:就是求多边形面积,可能是凹多边形,还有就是判断不能形成多边形的情况

解题思路:使用扫描线算法判断是否存在不相邻的两条边是否有交点,以及相邻的边是否重叠;

        使用的一些小trick

  •  线段相交快速算法:1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交   2.跨立试验  3.判断线段是否相邻
  • 顶点重合:扫描线算法以多边形顶点驱动,事件以顶点为键(我的实现虽然存的是数字,但还是根据顶点的位置进行比较排序的)存储在Event中, 如果最终Event 大小小于n_vertex,则说明有顶点重合
  • 面积使用叉积进行计算。如果最终的面积<least, 则说明有边重合,可以快速判断
  • 至于线段大小的比较,则是扫描线的一个难点。要求是:不同的线段比较之后仍然能保持:相邻的边在红黑树中仍然相邻相邻。这一点花了我80%的时间。在这个过程中,我参考了算法导论的答案,以及这个网址:http://www.eleves.ens.fr/home/cagne/internship_l3/mncubes_0/doc/html/index.html。使我非常受益
  • #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <cmath>
    #include <algorithm>
    #include <iostream>
    #include <map>
    #include "assert.h"
    #include <set>
    using namespace std;
    #define USE_HIGH_PERFORMANCE_IN_INPUT_DOUBLE 1
    // #define USE_WITNESS_POINT 1
    #define inf 1e12
    template<typename ValueType> class point;
    template<typename ValueType> class vec;
    template<typename ValueType> class line_segment;
    template<typename ValueType> class segment_compare;
    template<typename ValueType, int MaxSize> class polygon;
    template<typename T> bool operator< (const point<T>& left, const point<T>& right);
    template<typename T> bool operator> (const point<T>& left, const point<T>& right);
    template<typename T> bool operator== (const point<T>& left, const point<T>& right);
    template<typename T> vec<T> operator- (const vec<T>& left, const vec<T>& right);
    template<typename ValueType = double>
    class point
    {
    public:
        point(ValueType _x=0, ValueType _y=0)
            : x(_x), y(_y) {}
        virtual ~point() {}
        friend bool operator< <>(const point<ValueType>& left, const point<ValueType>& right);
        friend bool operator> <>(const point<ValueType>& left, const point<ValueType>& right);
    public:
        ValueType x,y;
    };
    template<typename ValueType = double>
    class vec : public point<ValueType>
    {
    public:
        typedef ValueType Type;
        vec(const Type& _x, const Type& _y)
            : point<Type>(_x,_y)
        {}
        vec(const point<Type>& val = point<Type>(0,0))
            : point<Type>(val)
        {}
        inline Type cross_product(const vec<Type>& right) const;
        inline Type dot_product(const vec<Type>& right) const;
        friend vec<Type> operator- <>(const vec<Type>& left, const vec<Type>& right);
    };
    template<typename ValueType>
    class line_segment
    {
    public:
        friend class segment_compare <ValueType>;
        template<typename T, int MaxSize> friend class polygon;
    public:
        line_segment(const point<ValueType>&, const point<ValueType>&);
        line_segment() { slope = inf; }
        bool is_crossed(const line_segment<ValueType>& r) const;
    #ifdef USE_WITNESS_POINT
        ValueType high(const point<ValueType>& p_sweep) const;
    #endif
        bool left_end_point(const point<ValueType>& tester) const { return tester == end_point[0]; }
    private:
        const vec<ValueType> v(int idx) const { return vec<ValueType>(end_point[idx]); }
        const point<ValueType>& se(int idx) const { return end_point[idx]; }
        point<ValueType> end_point[2];
        ValueType slope;
    };
    
    template<typename ValueType>
    class segment_compare
    {
    public:
        segment_compare(const point<ValueType> & __point) : pt(__point) {}
        bool operator() (const line_segment<ValueType>& a, const line_segment<ValueType>& b);
        const point<ValueType> & pt;
    };
    
    template<typename ValueType, int MaxSize = 1010>
    class polygon
    {
    private:
        class event_compare;
    public:
        polygon() :  Event(compare) ,compare(this)  { }
        bool read_init(int n);
        ValueType area();
        bool is_legal();
    private:
        point<ValueType> vertex[MaxSize+2]; // 1...n 存储了n个点, 0是1左边的点==【n】, n+1是n右边的点==[1];
        int n_vertex;
        line_segment<ValueType> edge[MaxSize];
        event_compare compare;
        set<int, event_compare> Event;
    };
    // template<typename ValueType, int MaxSize> polygon<ValueType,MaxSize>;
    template<typename ValueType, int MaxSize> bool 
        polygon<ValueType,MaxSize>::is_legal()
    {
        point<ValueType> pt;
        segment_compare<ValueType> cmp(pt);
        typedef  std::set<line_segment<ValueType>, segment_compare<ValueType> > segment_sweeping_store;
        segment_sweeping_store seg_set(cmp);
        for (typename set<int, event_compare>::const_iterator pos = Event.begin(), end=Event.end();
            pos != end; pos++)
        {
            int idx = *pos;
            assert(1<=idx && idx<=n_vertex);
            pt = vertex[idx];
            line_segment<ValueType> l1(vertex[idx], vertex[idx+1]), l2(vertex[idx],vertex[idx-1]);
            // 判断l1、l2是否重合
            if (l1.slope == l2.slope && (vec<ValueType>(vertex[idx+1])-vec<ValueType>(vertex[idx])).dot_product(vec<ValueType>(vertex[idx-1])-vec<ValueType>(vertex[idx])) >= 0)
                return false;
    #ifdef USE_WITNESS_POINT
            line_segment<ValueType> t(*pos, *pos);
            if (seg_set.size() >= 1) // 待插入的边是否和已有的边相交
            {
                typename segment_sweeping_store::iterator it = seg_set.upper_bound(t);
                std::reverse_iterator<typename segment_sweeping_store::iterator> rit(it);
    
                while (rit != seg_set.rend() && (*rit).high(pt) == pt.y)
                {
                    if ((*rit).is_crossed(l1) || (*rit).is_crossed(l2))
                        return false;
                    rit ++;
                }
                if (it != seg_set.end())
                {
    
                    if ((*it).is_crossed(l1) || (*it).is_crossed(l2))
                        return false; 
    
                }
    
            }
    #endif // USE_WITNESS_POINT
            bool is_l1_left_end = l1.left_end_point(vertex[idx]);
            bool is_l2_left_end = l2.left_end_point(vertex[idx]);
            if (is_l1_left_end)
            {
                pair< typename segment_sweeping_store::iterator, bool > t(seg_set.insert(l1));
                if(t.second == false)
                    return false;
                typename segment_sweeping_store::iterator f(t.first);
                if (++f != seg_set.end() && l1.is_crossed(*f)) return false;
                if (f != seg_set.begin() && l1.is_crossed(*--f)) return false;
            }
            if (is_l2_left_end)
            {
                pair< typename segment_sweeping_store::iterator, bool > t(seg_set.insert(l2));
                if(t.second == false)
                    return false;
                typename segment_sweeping_store::iterator f(t.first);
                if (++f != seg_set.end() && l2.is_crossed(*f)) return false;
                if (f != seg_set.begin() && l2.is_crossed(*--f)) return false;
            };
            if (!is_l1_left_end)
            {
                typename segment_sweeping_store::iterator f,pre,after;
                f = seg_set.find(l1);
                if (f != seg_set.end())
                {
                    if (f!=seg_set.begin()) // 删除f后,需检查上下两条线段
                    {
                        after = f; after++;
                        if (after != seg_set.end())
                        {
                            pre = f; pre--;
                            if (pre->is_crossed(*after))
                            {
                                return false;
                            }
                        }
                    }
                    seg_set.erase(f);
                }
                else
                {
                    return false;
                }
            }
            if (!is_l2_left_end)
            {
                typename segment_sweeping_store::iterator f,pre,after;
                f = seg_set.find(l2);
                if (f != seg_set.end())
                {
    
                    if (f!=seg_set.begin())
                    {
                        after = f; after++;
                        if (after != seg_set.end())
                        {
                            pre = f; pre--;
                            if (pre->is_crossed(*after))
                            {
                                return false;
                            }
                        }
                    }
                    seg_set.erase(f);
                }
                else
                {
                    return false;
                }
            }
        }
        return true;
    }
    template<typename ValueType, int MaxSize> bool 
        polygon<ValueType,MaxSize>::read_init(int n)
    {
        Event.clear();
        n_vertex = n;
        for (int i=1; i<=n; i++)
        {
    #ifdef USE_HIGH_PERFORMANCE_IN_INPUT_DOUBLE
            scanf("%lf %lf", &vertex[i].x, &vertex[i].y);
    #else
            std::cin >> vertex[i].x >> vertex[i].y;
    #endif // USE_HIGH_PERFORMANCE_IN_INPUT_DOUBLE
            Event.insert(i);
        }
        vertex[0] = vertex[n];
        vertex[n+1] = vertex[1];
        return Event.size() == n;
    }
    template<typename ValueType, int MaxSize> ValueType 
        polygon<ValueType,MaxSize>::area()
    {
        ValueType ans = 0;
        for (int i=1; i<=n_vertex; i++)
        {
            ans += vec<ValueType>(vertex[i]).cross_product(vertex[i+1]);
        }
        return 0.5 * fabs(double(ans));
    }
    template<typename ValueType, int MaxSize> class
        polygon<ValueType,MaxSize>::event_compare
    {
    public:
        event_compare(const polygon* const __poly) : 
            poly(__poly) {}
        bool operator() (const int& left, const int& right) const
        {
            return poly->vertex[left] < poly->vertex[right];
        }
        const polygon* const poly;
    };
    
    template<typename ValueType> bool
        segment_compare<ValueType>::operator() (const line_segment<ValueType>& a, const line_segment<ValueType>& b)
    {
        // 判断两线段在扫描线上的大小
        if (a.se(0) > b.se(0) || (!(a.se(0) < b.se(0)) && a.se(1) > b.se(1)))
            return !(*this)(b,a);
        double ans = (b.v(0)-a.v(1)).cross_product(a.v(1)-a.v(0));
        if (ans) return ans < 0;
        // else  b.0 位于a上
        ans = (b.v(1)-a.v(0)).cross_product(a.v(1)-a.v(0));
        if (ans)  return ans < 0;
        // else a、b共线
    #ifdef USE_WITNESS_POINT
        if (a.slope != b.slope)
            return a.slope < b.slope; // 其中一个是测试点
    #endif // USE_WITNESS_POINT
    
        return (a.se(1).y < b.se(1).y) || (a.se(1).x < b.se(1).x); // 解决水平或者竖直的情况
    }
    template<typename ValueType>
    line_segment<ValueType>::line_segment(const point<ValueType>& l, const point<ValueType>& r)
    {
        if (l > r)
            end_point[0] = r, end_point[1] = l;
        else end_point[0] = l, end_point[1] = r;
        if (end_point[0].x != end_point[1].x)
            slope = (end_point[1].y-end_point[0].y) / (end_point[1].x-end_point[0].x);
        else slope = inf;
    }
    template<typename ValueType> bool
        line_segment<ValueType>::is_crossed(const line_segment<ValueType>& r) const
    {
    #define max std::max<ValueType>
    #define min std::min<ValueType>
        bool ans = max(se(0).x, se(1).x) >= min(r.se(0).x, r.se(1).x) && // 矩形快速排斥
            max(r.se(0).x, r.se(1).x) >= min(se(0).x, se(1).x) &&
            max(se(0).y, se(1).y) >= min(r.se(0).y, r.se(1).y) &&
            max(r.se(0).y, r.se(1).y) >= min(se(0).y, se(1).y) &&
            (v(0)-r.v(0)).cross_product(r.v(1)-r.v(0)) * (r.v(1)-r.v(0)).cross_product(v(1)-r.v(0)) >= ValueType(0)  && // 跨立排斥
            (v(1)-v(0)).cross_product(r.v(0)-v(0)) *  (r.v(1)-v(0)).cross_product(v(1)-v(0))  >= ValueType(0) ;
        
        return ans && !(this->end_point[0] == r.end_point[0] || this->end_point[1] == r.end_point[0] ||
            this->end_point[0] == r.end_point[1] || this->end_point[1] == r.end_point[1]);
    }
    #ifdef USE_WITNESS_POINT
    template<typename ValueType>
    ValueType line_segment<ValueType>::high(const point<ValueType>& p_sweep) const
    {
        if (se(0).x == se(1).x)
        {
            // 线段依附在扫描线上,输出线段上与psweep最近的点
            assert(se(1).y >= se(0).y);
            if (p_sweep.y < se(0).y)
                return se(0).y;
            else if (p_sweep.y > se(1).y)
                return se(1).y;
            return p_sweep.y;
        }
        else
        {
            // 输出线段与X=p_sweep.x的交点
            return slope * (p_sweep.x - se(0).x) + se(0).y;
        }
    }
    #endif
    
    template<typename ValueType>
    ValueType vec<ValueType>::cross_product(const vec<ValueType>& right) const
    {
        return point<ValueType>::x*right.point<ValueType>::y - point<ValueType>::y*right.point<ValueType>::x;
    }
    template<typename ValueType>
    ValueType vec<ValueType>::dot_product(const vec<ValueType>& right) const
    {
        return point<ValueType>::x*right.point<ValueType>::x + point<ValueType>::y*right.point<ValueType>::y;
    }
    template<typename T>
    vec<T> operator- (const vec<T>& left, const vec<T>& right)
    {
        return vec<T>(left.x-right.x, left.y-right.y);
    }
    template<typename T>
    bool operator< (const point<T>& left, const point<T>& right)
    {
        return left.x == right.x ? left.y < right.y : left.x < right.x;
    }
    template<typename T>
    bool operator> (const point<T>& left, const point<T>& right)
    {
        return left.x == right.x ? left.y > right.y : left.x > right.x;
    }
    template<typename T> bool operator== (const point<T>& left, const point<T>& right)
    {
        return (left.x == right.x) && (left.y == right.y );
    }
    
    int main()
    {
        int n;
        //freopen("D:\\1.in", "r+", stdin);
        int counter = 0;
        typedef double M_DOUBLE;
        while (
    #ifdef USE_HIGH_PERFORMANCE_IN_INPUT_DOUBLE
            scanf("%d",&n) != EOF
    #else
            std::cin >> n
    #endif // USE_HIGH_PERFORMANCE_IN_INPUT_DOUBLE
            && n>0)
        {
            polygon<M_DOUBLE> t;
            M_DOUBLE area;
            // 输入点,计算面积, 编织扫描线事件,判断是否有重合的点(若有则返回-1),数据存储在static字段中
    
            bool flaglegal = t.read_init(n) && n>=3 && ((area=t.area()) > 1e-12) && t.is_legal();
            if (counter >= 1)
                printf("\n"); // 打印中间换行
            counter++;
            printf("Figure %d: ", counter);
            if (flaglegal)
            {
                printf("%.2lf\n", double(area));
            }
            else
            {
                printf("Impossible\n");
            }
        }
        return 0;
    }



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值