POJ 2284 That Nice Euler Circuit (LA 3263 HDU 1665)

66 篇文章 0 订阅
58 篇文章 0 订阅

http://poj.org/problem?id=2284

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1264

http://acm.hdu.edu.cn/showproblem.php?pid=1665

题目大意:

平面上有一个包含n个端点的一笔画,第n个端点总是和第一个端点重合,因此图案是一条闭合的曲线。组成一笔画的线段可以相交,但是不会重合。求这些线段将平面分成多少部分。

\epsfbox{p3263.eps}

思路:

刘汝佳书上原题。我基本抄他的,几何题没经验T T。还学到了unique的用法,去除相邻相同的元素

欧拉定理有:设平面图的顶点数、边数、面数分别V,E,F则V+F-E=2(上个学期刚学的离散数学,现学现卖啊!)

如上面的图,右边的E=12,V=9,F=5.

平面图的结点由两部分组成,即原来顶点和新增加的顶点,因为可能出现三点共线,所以要删除重复点。


#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=300+10;
const double eps = 1e-10;
int dcmp(double x) //判断浮点数是否等于0,或者小于大于0
{
  if(fabs(x) < eps) 
	  return 0; 
  else 
	  return x < 0 ? -1 : 1;
}
struct Point 
{
  double x, y;
  Point(double x=0, double y=0):x(x),y(y) { }

};
typedef Point Vector;
Vector operator + (const Vector& A, const Vector& B) { return Vector(A.x+B.x, A.y+B.y); }
Vector operator - (const Point& A, const Point& B) { return Vector(A.x-B.x, A.y-B.y); }
Vector operator * (const Vector& A, double p) { return Vector(A.x*p, A.y*p); }
double Dot(const Vector& A, const Vector& B) { return A.x*B.x + A.y*B.y; }
double Cross(const Vector& A, const Vector& B) { return A.x*B.y - A.y*B.x; }

//两直线交点,参数式。
Point GetLineIntersection(const Point& P, const Vector& v, const Point& Q, const Vector& w) { 
  Vector u = P-Q;
  double t = Cross(w, u) / Cross(v, w);
  return P+v*t;
}
//两直线是否有交点,两点式。
bool SegmentProperIntersection(const Point& a1, const Point& a2, const Point& b1, const Point& b2) {
  double c1 = Cross(a2-a1,b1-a1), c2 = Cross(a2-a1,b2-a1),
  c3 = Cross(b2-b1,a1-b1), c4=Cross(b2-b1,a2-b1);
  return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;
}
//点在线段上的判定,两点式
bool OnSegment(const Point& p, const Point& a1, const Point& a2) {
  return dcmp(Cross(a1-p, a2-p)) == 0 && dcmp(Dot(a1-p, a2-p)) < 0;
}

bool operator <(const Point &a,const Point &b)
{
	return a.x<b.x||a.x==b.x && a.y<b.y;
}
bool operator ==(const Point &a,const Point &b)
{
	return a.x==b.x && a.y==b.y;
}
Point v[MAXN*MAXN],p[MAXN];
int main()
{
	int n,kase=1;
	while(~scanf("%d",&n),n)
	{
		for(int i=0;i<n;i++)
		{
			scanf("%lf%lf",&p[i].x,&p[i].y);
			v[i]=p[i];
		}

		int len=n--,e=n;
		for(int i=0;i<n;i++)
			for(int j=i+1;j<n;j++)
			{
				if(SegmentProperIntersection(p[i],p[i+1],p[j],p[j+1]))
					v[len++]=GetLineIntersection(p[i],p[i+1]-p[i],p[j],p[j+1]-p[j]);
			}

		sort(v,v+len);
		int c=unique(v,v+len)-v; //去除重复的点
		for(int i=0;i<c;i++)
			for(int j=0;j<n;j++)
				if(OnSegment(v[i],p[j],p[j+1])) e++;

		//v+f-e=2 f=e+2-v
		printf("Case %d: There are %d pieces.\n",kase++,e+2-c);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值