USACO SECTION 3.4 Electric Fence

Electric Fence
Don Piele

In this problem, `lattice points' in the plane are points with integer coordinates.

In order to contain his cows, Farmer John constructs a triangular electric fence by stringing a "hot" wire from the origin (0,0) to a lattice point [n,m] (0<=;n<32000, 0<m<32000), then to a lattice point on the positive x axis [p,0] (p>0), and then back to the origin (0,0).

A cow can be placed at each lattice point within the fence without touching the fence (very thin cows). Cows can not be placed on lattice points that the fence touches. How many cows can a given fence hold?

PROGRAM NAME: fence9

INPUT FORMAT

The single input line contains three space-separated integers that denote n, m, and p.

SAMPLE INPUT (file fence9.in)

7 5 10

OUTPUT FORMAT

A single line with a single integer that represents the number of cows the specified fence can hold.

SAMPLE OUTPUT (file fence9.out)

20

 

/*
ID: conicoc1
LANG: C
TASK: fence9
*/

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>

#define MaxX 1000000
#define EP 1e-10

typedef struct Point   //点结构 
{
	double x,y;
}Point;

typedef struct Line    //线段结构 
{
	Point s,e;
}Line;

double Max(double a,double b)
{
	return a>b?a:b;
}

double Min(double a,double b)
{
	return a>b?b:a;
}

/*计算向量的叉积*/
double Multiply(Point sp,Point ep,Point op)
{
	return  (ep.x-op.x)*(sp.y-op.y)-(ep.y-op.y)*(sp.x-op.x);  
/*返回值>0,sp在opep的顺时针方向。<0,sp在opep的逆时针方向。=0共线,可能同向也可能逆向*/ 
}

/* 判断点是否在线段上*/
int OnLine(Line l,Point P) 
{
/*要求点在线段所在的直线上,且点在以线段为对角线的矩形中*/ 
	if(Multiply(l.s,l.e,P)==0&&(P.x-l.s.x)*(P.x-l.e.x)<=0&&(P.y-l.s.y)*(P.y-l.e.y)<=0)
	   return 1;
 	else	
	   return 0;
}

/* 判断两条线段是否相交*/
int Intersect(Line u,Line v)
{
/*要求通过排斥实验和跨立实验*/ 
	return ( (Min(u.s.x,u.e.x)<=Max(v.s.x,v.e.x))&&
	         (Min(v.s.x,v.e.x)<=Max(u.s.x,u.e.x))&&
	         (Min(u.s.y,u.e.y)<=Max(v.s.y,v.e.y))&&
	         (Min(v.s.y,v.e.y)<=Max(u.s.y,u.e.y))&&
             (Multiply(v.s,u.s,v.e)*Multiply(u.e,v.s,v.e)>=0)&&
	         (Multiply(u.s,v.s,u.e)*Multiply(v.e,u.s,u.e)>=0)
	        );
 /*此相交包括顶点相交*,如需要使用非顶点相交,在条件中加上以下条件:       */ 
 /*     !OnLine(u,v.e)&&!OnLine(u,v.s)&&!Online(v,u.e)&&!Online(v,u.s)    */
} 

/*
    射线法计算点是否在多边形内
为了统一,我们在计算射线L和多边形的交点时:
1。对于多边形的水平边不作考虑;
2。对于多边形的顶点和L相交的情况,如果该顶点是其所属的边上纵坐标较大的顶点,则计数,否则忽略;
3。对于P在多边形边上的情形,直接可判断P属于多边行。
*/

int IsInPolygon(Point P,int vcount,Point V[])
{
	Line u,v;
	u.s.x=P.x;
	u.s.y=P.y;
	u.e.x=MaxX;
	u.e.y=P.y;
	int i,j,count=0;
	for(i=0;i<vcount;i++){
		j=(i+1)%vcount;
		v.s.x=V[i].x;
		v.s.y=V[i].y;
		v.e.x=V[j].x;
		v.e.y=V[j].y;
		if(OnLine(v,P))    //判断P是否在某条边上 
			return 0;
		if(fabs(v.s.y-v.e.y)>EP){ //判断边是否水平 
			if((OnLine(u,V[i])&&V[i].y>V[j].y)||OnLine(u,V[j])&&V[j].y>V[i].y)
				count++;
			else
			if(Intersect(u,v))
				count++;         
		}
	}
		return count%2==1;
}
/********************计算几何模板*************************/ 

int main()
{
	FILE *fin,*fout;
	fin=fopen("fence9.in","r");
	fout=fopen("fence9.out","w");
	double n,m,p;
	fscanf(fin,"%lf %lf %lf",&n,&m,&p);
	Point P[3],Left,Right;
	P[0].x=P[0].y=0;
	P[1].x=n,P[1].y=m;
	P[2].x=p,P[2].y=0;
	int i,j,Ans=0;
	Left.x=0,Left.y=1;
	Right.x=p,Right.y=1;
	if(n>p){
		for(i=1;i<m;i++){
			while(IsInPolygon(Right,3,P)||Right.x<=n/m*i)
				Right.x++;
			while(!IsInPolygon(Left,3,P)&&Left.x<Right.x)
				Left.x++;
			Ans+=Right.x-Left.x;
//			printf("%f %f %d\n",Left.x,Right.x,Ans);
			Left.y++;
			Right.y++;	
		}
	}
	else{
		for(i=1;i<m;i++){
			while(!IsInPolygon(Left,3,P)&&Left.x<=Right.x)
				Left.x++;
			while(!IsInPolygon(Right,3,P)&&Left.x<=Right.x)
				Right.x--;
			Ans+=Right.x-Left.x+1;
			Left.y++;
			Right.y++;	
		}
	}
	fprintf(fout,"%d\n",Ans);
	return 0;
}


这种题目我居然用了计算几何。。计算几何。。结果是什么。。。判断点在三角形内。。。各种乱七八糟的特殊情况+判定

做的各种劳累啊。。。代码还一长串。。。

 

好了,我们来看题目的常规解法

方法一:皮克定理。。。(这是什么东西?)

皮克定理:  面积S和内部格点数目a、边上格点数目b的关系:S = a + b/2 - 1。

另外还要运用一个定理:一条直线((0,0),(n,m))上的格点数等于n与m的最大公约数+1。

这样子很快就出来的

方法二:类似我上面用的方法,但是要求两条直线的斜率,且要讨论斜率不存在的特殊情况

然后枚举每个横坐标或者纵坐标计算一次有多少个点符合要求。全部加起来就是所求的答案

另外,枚举x坐标的方法可以用二分法优化,找到最接近线段(0,0)->(m,n)的点

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值