题目: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; }