POJ 3348 Cows

题目大意:

        一农场主想用牧场上的几颗树作为栏杆将牧场围起来防止牛逃跑,用直线将最外围的栏杆连起来可以围出一个最大面积,已知一头牛想要存活至少需要50平方米的面积。

       现只有一个测例,测例中给出树的数量n(1 ≤ n ≤ 10,000),以及每棵数的坐标(x, y),其中x, y∈[-1000, 1000]而且是整数,现求出最多能样多少头牛。

题目链接

注释代码:

/*        
 * Problem ID : POJ 3348 Cows
 * Author     : Lirx.t.Una        
 * Language   : C++     
 * Run Time   : 0 ms        
 * Run Memory : 264 KB        
*/ 

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>

//maximum number of trees
//树的最大数量
#define	MAXTREEN		10000

using namespace std;

struct	Point {//平面点

	int		x, y;

	Point(void) {}

	Point( int xx, int yy ) : x(xx), y(yy) {}

	friend istream &
	operator>>( istream &is, Point &p ) {//输入
	
		is >> p.x >> p.y;

		return is;
	}

	Point
	operator-(const Point &oth)
	const {//计算向量,返回向量
	
		return Point( x - oth.x, y - oth.y );
	}

	int
	operator*(Point oth) {//计算叉积
	
		return x * oth.y - oth.x * y;
	}

	bool
	operator<(Point &p) {//用于筛选y最小其次x也最小的点,即凸包中的最低最左点P0

		return y < p.y || ( y == p.y && x < p.x );
	}

	bool
	operator>=(Point &p) {//用于去掉相对于P0极角相等的离P0较近的点

		return abs(x) >= abs(p.x) && abs(y) >= abs(p.y);
	}
};

Point	pt[MAXTREEN];//point,表示树的点集
Point	stk[MAXTREEN];//栈,Graham算法中使用
int		top;//栈顶

void
swp( Point *ap, int i, int j ) {//交换pt数组中的两元素

	Point	ptmp;

	ptmp  = ap[i];
	ap[i] = ap[j];
	ap[j] = ptmp;
}

bool
fcmp(const Point &p1, const Point &p2) {//用于对点的排序
	//对于任何两点,比较其相对于P0极角的大小可以使用叉积法

	//flag = P0->P1 × P0->P2
	//flag > 0表示P2极角大于P1,从图上看P2靠左、P1靠右
	//flag = 0表示P1、P2、P0共线
	//flag < 0表示P1极角大于P2,从图上看P1靠左、P2靠右
	
	return ( p1 - *pt ) * ( p2 - *pt ) > 0;
}

void
graham(int n) {//Gramham扫描法,找出凸包,凸包按逆时针保存在栈stk里

	int		i;

	stk[0] = pt[0];
	stk[1] = pt[1];
	stk[2] = pt[2];

	top = 2;

	for ( i = 3; i < n; i++ ) {
	
		//注意要使用>=,比>更优化,以为如果出现==的情况就意味着i、top、top - 1共线
		//为了避免计算面积时多处理几步,所以只保留离top - 1较远的i而把top弹掉
		while ( ( pt[i] - stk[top - 1] ) * ( stk[top] - stk[top - 1] ) >= 0 )
			top--;//表示stk[top]处凹陷进去了,因此不符合要求,要弹栈
		//由于弹的永远都是中间点,因此0不可能被弹,所以不用对栈判空
		
		stk[++top] = pt[i];//总算凸了
	}
}

int
dArea(void) {//double area,计算凸包的面积的两倍

	int		s;
	int		i;

	//凸包的面积就是按照逆时针每相邻两点对P0的叉积绝对值之和
	for ( s = 0, i = 1; i < top; i++ )
		s +=  abs( ( stk[i] - *stk ) * ( stk[i + 1] - *stk ) );

	return s;
}

int
main() {

	int		n;//顶点数

	int		i, j;//计数变量
	int     mi;//minimum ith,最下然后最左的顶点的下标

	Point	pi, pj;//临时变量

	scanf("%d", &n);
	if ( n < 3 ) {//判断一下特殊情况

		puts("0");
		return 0;
	}

	for ( i = 0; i < n; i++ ) cin >> pt[i];

	for ( mi = 0, i = 1; i < n; i++ )
		if ( pt[i] < pt[mi] )
			mi = i;
	swp( pt, 0, mi );//选出最下然后最左的点和0号点交换,0号点击P0
	sort(pt + 1, pt + n, fcmp);//按照对P0的极角从小到大排序
	for ( j = 1, i = 2; i < n; i++ ) {//!!注意:快速排序时不稳定的
		//因此要从所有极角相等的点中剔除和P0距离较近的点保留距离最大的点

		if ( !( ( pi = pt[i] - *pt ) * ( pj = pt[j] - *pt ) ) ) {//找出极角相等的点
			//即和P0共线的点

			if ( pi >= pj ) pt[j] = pt[i];//计算绝对距离
			continue;
		}
		
		pt[++j] = pt[i];
	}
	n = j + 1;//离散化后实际可用点数有所减少

	graham(n);
	//牛存活面积为50,由于凸包面积是两倍的,因此牛的面积也要相应乘2
	printf("%d\n", dArea() / 100);

	return 0;
}

无注释代码:

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>

#define	MAXTREEN		10000

using namespace std;

struct	Point {

	int		x, y;

	Point(void) {}

	Point( int xx, int yy ) : x(xx), y(yy) {}

	friend istream &
	operator>>( istream &is, Point &p ) {
	
		is >> p.x >> p.y;

		return is;
	}

	Point
	operator-(const Point &oth)
	const {
	
		return Point( x - oth.x, y - oth.y );
	}

	int
	operator*(Point oth) {
	
		return x * oth.y - oth.x * y;
	}

	bool
	operator<(Point &p) {

		return y < p.y || ( y == p.y && x < p.x );
	}

	bool
	operator>=(Point &p) {

		return abs(x) >= abs(p.x) && abs(y) >= abs(p.y);
	}
};

Point	pt[MAXTREEN];
Point	stk[MAXTREEN];
int		top;

void
swp( Point *ap, int i, int j ) {

	Point	ptmp;

	ptmp  = ap[i];
	ap[i] = ap[j];
	ap[j] = ptmp;
}

bool
fcmp(const Point &p1, const Point &p2) {
	
	return ( p1 - *pt ) * ( p2 - *pt ) > 0;
}

void
graham(int n) {

	int		i;

	stk[0] = pt[0];
	stk[1] = pt[1];
	stk[2] = pt[2];

	top = 2;

	for ( i = 3; i < n; i++ ) {
	
		while ( ( pt[i] - stk[top - 1] ) * ( stk[top] - stk[top - 1] ) >= 0 )
			top--;
		
		stk[++top] = pt[i];
	}
}

int
dArea(void) {

	int		s;
	int		i;

	for ( s = 0, i = 1; i < top; i++ )
		s +=  abs( ( stk[i] - *stk ) * ( stk[i + 1] - *stk ) );

	return s;
}

int
main() {

	int		n;
	int		i, j;
	int     mi;

	Point	pi, pj;

	scanf("%d", &n);
	if ( n < 3 ) {

		puts("0");
		return 0;
	}

	for ( i = 0; i < n; i++ ) cin >> pt[i];

	for ( mi = 0, i = 1; i < n; i++ )
		if ( pt[i] < pt[mi] )
			mi = i;
	swp( pt, 0, mi );
	sort(pt + 1, pt + n, fcmp);
	for ( j = 1, i = 2; i < n; i++ ) {

		if ( !( ( pi = pt[i] - *pt ) * ( pj = pt[j] - *pt ) ) ) {

			if ( pi >= pj ) pt[j] = pt[i];
			continue;
		}
		
		pt[++j] = pt[i];
	}
	n = j + 1;

	graham(n);
	printf("%d\n", dArea() / 100);

	return 0;
}

单词解释:

fence:n, 栅栏,篱笆

plowshare:n, 犁头

sword:n, 剑

adventure:n, 历险,冒险

post:n, 标杆

pasture:n, 牧场,草场

oversee:vt, 监督,审查

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值