题目大意:
一农场主想用牧场上的几颗树作为栏杆将牧场围起来防止牛逃跑,用直线将最外围的栏杆连起来可以围出一个最大面积,已知一头牛想要存活至少需要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, 监督,审查