zoj 1460 The Partition of a Cake

输入坑姐啊!!!

因为采用的是离散数学上图论的方法,也就是类似http://blog.csdn.net/zxy_snow/article/details/6581745这个题的方法,这个求蛋糕的块数,也就是这个平面图的面数 - 1.

根据欧拉定理,连通平面图(如果把交点都看做结点,那么肯定是平面图)满足 V - E + R = 2。 其中V是顶点数,E是边数,R是面数。

所以直接找所有的交点(不能重复,我排了下序去重),找边数的话,画图可知,一条线段上有N个交点,那么它就被分成了 N-1份~所以只要这么找就可以了(这点要求必须所有的线段都不重合~!)。

坑姐啊!!!题目上说,The intersections of the cut line and the cake edge are two.而且输入数据都是线段在边上有两点的,所以我以为线段的两个端点一定在矩形的边上的 = = 。。。刚才测试了下,如果不在边上的话,让程序执行while(1); 结果还真TLE了,说明数据线段的两点可能不在边上,坑姐啊!!那么计算交点的时候,得判断这个交点是否在这个矩形内(包括边界)!

还有一点要注意,可能切痕有重叠,所以一定要判断切痕是否出现过 = =。。。而且鉴于切痕端点是随便给的,所以要判断是否共线 = = 而不是判断相等(我没试只判断相等对不对,只试了,它里面肯定有重叠切痕!)

另一种方法,是蓝书上的,http://blog.csdn.net/program_shun/article/details/6589452和这个题解差不多吧,不过蓝书上有证明。(《实用算法分析设计》)。

刚删了自己写的去重的程序,用了unique函数,好方便哈,unique只能去掉相邻重复的元素留下一个,所以必须排序后再用unique,写了下unique的比较函数,这个也比较方便哈,STL好伟大~~

#include <queue>
#include <stack>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <limits.h>
#include <string.h>
#include <string>
#include <algorithm>

using namespace std;

const int MAX = 10;
struct point{ double x,y;};
const double eps = 1e-8;
bool dy(double x,double y)	{	return x > y + eps;}	// x > y 
bool xy(double x,double y)	{	return x < y - eps;}	// x < y 
bool dyd(double x,double y)	{ 	return x > y - eps;}	// x >= y 
bool xyd(double x,double y)	{	return x < y + eps;} 	// x <= y 
bool dd(double x,double y) 	{	return fabs( x - y ) < eps;}  // x == y
double crossProduct(point a,point b,point c)//向量 ac 在 ab 的方向 
{
	return (c.x - a.x)*(b.y - a.y) - (b.x - a.x)*(c.y - a.y);
}
point p[MAX*MAX];
struct line{ point a,b; 
			void p(double x,double y,double x1,double y1)
			{
				a.x = x; a.y = y;
				b.x = x1; b.y = y1;
			}
};
line l[MAX];
int ee;
bool cmp(point a,point b)
{
	if( dd(a.x,b.x) ) return xy(a.y,b.y);
	return xy(a.x,b.x);
}
bool cmp1(point a,point b)
{
	return dd(a.x,b.x) && dd(a.y,b.y);
}
bool onSegment(point a, point b, point c)
{
	double maxx = max(a.x,b.x);
	double maxy = max(a.y,b.y);
	double minx = min(a.x,b.x);
	double miny = min(a.y,b.y);
	if( dd(crossProduct(a,b,c),0.0) && dyd(c.x,minx) && xyd(c.x,maxx) && dyd(c.y,miny) && xyd(c.y,maxy) )
		return true;
	return false;
}
bool s2s_inst(point p1,point p2, point p3, point p4) 
{
	double d1 = crossProduct(p3,p4,p1);
	double d2 = crossProduct(p3,p4,p2);
	double d3 = crossProduct(p1,p2,p3);
	double d4 = crossProduct(p1,p2,p4);
	if( xy(d1 * d2,0.0) && xy(d3 * d4,0.0) )	return true;
	if( dd(d1,0.0) && onSegment(p3,p4,p1) )		return true;//如果不判端点相交,则下面这四句话不需要 
	if( dd(d2,0.0) && onSegment(p3,p4,p2) )		return true;
	if( dd(d3,0.0) && onSegment(p1,p2,p3) )		return true;
	if( dd(d4,0.0) && onSegment(p1,p2,p4) )		return true;
	return false;
}
point s2s_inst_p(point u1,point u2,point v1,point v2)
{
	point ans = u1;
	double t = ((u1.x - v1.x)*(v1.y - v2.y) - (u1.y - v1.y)*(v1.x - v2.x))/
				((u1.x - u2.x)*(v1.y - v2.y) - (u1.y - u2.y)*(v1.x - v2.x));
	ans.x += (u2.x - u1.x)*t;
	ans.y += (u2.y - u1.y)*t;
	return ans;
}
void check(point x,int n)
{
	for(int i=0; i<n; i++)
		if( onSegment(l[i].a,l[i].b,x) )
			ee++;
}
bool chong(point a,point b,int n)
{
	for(int i=0; i<n; i++)
		if( dd(crossProduct(l[i].a,l[i].b,a),0.0) 
			&& dd(crossProduct(l[i].a,l[i].b,b),0.0) )
			return true;
	return false;
}
int main()
{
	l[0].p(0,0,1000,0); l[1].p(0,0,0,1000); 
	l[2].p(0,1000,1000,1000); l[3].p(1000,0,1000,1000);
	
	int n;
	double a,b,c,d;
	while( ~scanf("%d",&n) && n )
	{
		int cnt = 0;
		ee = 0;
		int cc = 4;
		while( n-- )
		{
			scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
			point aa,bb; aa.x = a; aa.y = b; bb.x = c; bb.y = d;
			if( !chong(aa,bb,cc) )
				l[cc++].p(a,b,c,d);
		} 
		n = cc;	
		for(int i=0; i<n; i++)
			for(int k=i+1; k<n; k++)
				if( s2s_inst(l[i].a,l[i].b,l[k].a,l[k].b) )
				{
					point pp = s2s_inst_p(l[i].a,l[i].b,l[k].a,l[k].b);
					if( dyd(pp.x,0.0)  && xyd(pp.x,1000.0) && dyd(pp.y,0.0) && xyd(pp.y,1000.0) )
						p[cnt++] = pp;
				}

		sort(p,p+cnt,cmp);
		int nn = unique(p,p+cnt,cmp1) - p;// unique函数好好用~~ 
		
		for(int i=0; i<nn; i++)
			check(p[i],n);	// 找边的数目 
			
		int ans = ee - n - nn + 1;
		printf("%d\n",ans);
	}

return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值