您的位置:首页 > 其它

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

2013-05-04 01:59 375 查看
题目: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
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: