凸包板子:
const int maxn = 1e5 + 5;
struct point {
int x, y;
friend int operator * (const point& a,const point& b){
return a.x*b.y - a.y*b.x;
}
bool operator < (const point &_) const {
if(_.x == x) return y < _.y;
return x < _.x;
}
}tb[maxn], po[maxn];
point xl(const point& a, const point& b) {
return point{b.x-a.x, b.y-a.y};
}
ll dis(point a, point b) { // 两点之间的距离
return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}
void solve() {
int n; scanf("%d",&n);
for(int i = 1 ; i <= n ; i ++) {
scanf("%d%d",&po[i].x, &po[i].y);
}
sort(po+1, po+n+1);
int k = 0;
for(int i = 1 ; i <= n ; i ++) {
while(k > 1 && xl(tb[k-1], po[i]) * xl(tb[k-1], tb[k]) > 0) --k;
tb[++k] = po[i];
}
int tmp = k;
for(int i = n - 1 ; i >= 1 ; i --) {
while(k > tmp && xl(tb[k-1], po[i]) * xl(tb[k-1], tb[k]) > 0) --k;
tb[++k] = po[i];
}
if (n > 1) -- k;
printf("%d\n", k);
for(int i = 1 ; i <= k ; i ++){
printf("%d %d\n",tb[i].x,tb[i].y);
}
}
解释版:
const int maxn = 1e5 + 5;
struct point {
int x, y;
friend int operator * (const point& a,const point& b){
return a.x*b.y - a.y*b.x;
} //重载*为叉积.
bool operator < (const point &_) const {
if(_.x == x) return y < _.y;
return x < _.x;
} //水平排序, 先让x小的排前面. 然后再是y小的排前面.
}tb[maxn], po[maxn];
point xl(const point& a, const point& b) { // 向量ab, a->b;
return point{b.x-a.x, b.y-a.y};
}
ll dis(point a, point b) {
return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}
void solve() {
int n; scanf("%d",&n);
for(int i = 1 ; i <= n ; i ++) {
scanf("%d%d",&po[i].x, &po[i].y);
}
sort(po+1, po+n+1);
int k = 0;
for(int i = 1 ; i <= n ; i ++) {
while(k > 1 && xl(tb[k-1], po[i]) * xl(tb[k-1], tb[k]) > 0) --k; //= 是判断是否共线的.
tb[++k] = po[i]; //大于还是小于0自己定, 都是可以的, 并且你会发现上下的规则是一样的.
//唯一的不同就是最后给出的凸包顺序是逆时针还是顺时针.
//随便画一个图也可以知道一遍扫完只形成了一半的凸包, 所以还需要反着在扫一遍.
}
int tmp = k;
for(int i = n - 1 ; i >= 1 ; i --) {
while(k > tmp && xl(tb[k-1], po[i]) * xl(tb[k-1], tb[k]) > 0) --k;
tb[++k] = po[i];
}
if (n > 1) -- k; //起点算了两次.
printf("%d\n", k); //凸包的点数.
for(int i = 1 ; i <= k ; i ++){
printf("%d %d\n",tb[i].x,tb[i].y);
}
}
凸包的最经典的应用莫过于是旋转卡壳. O(nlogn)的复杂度求平面最远点对的距离.
板子 :
模板题POJ2187
const int maxn = 1e5 + 5;
struct point {
int x, y;
friend int operator * (const point& a, const point& b){
return a.x*b.y - a.y*b.x;
}
bool operator < (const point &_) const {
if(_.x == x) return y < _.y;
return x < _.x;
}
}tb[maxn], po[maxn];
point xl(const point& a, const point& b) {
return point{b.x-a.x, b.y-a.y};
}
ll dis(point a, point b) {
return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}
void solve() {
int n;
while(~scanf("%d", &n)){
for(int i = 1 ; i <= n ; i ++) {
scanf("%d%d",&po[i].x, &po[i].y);
}
sort(po+1, po+n+1);
int k = 0;
for(int i = 1 ; i <= n ; i ++) {
while(k > 1 && xl(tb[k-1], po[i]) * xl(tb[k-1], tb[k]) >= 0) --k;
tb[++k] = po[i];
}
int tmp = k;
for(int i = n - 1 ; i >= 1 ; i --) {
while(k > tmp && xl(tb[k-1], po[i]) * xl(tb[k-1], tb[k]) >= 0) --k;
tb[++k] = po[i];
}
if (n > 1) -- k;
int x = 3; tb[k+1] = tb[1];
ll ans = 0;
for(int i = 1 ; i <= k ; i++) {
while(xl(tb[x], tb[i]) * xl(tb[x], tb[i+1]) <
xl(tb[x%k+1], tb[i]) * xl(tb[x%k+1], tb[i+1])){
x = x%k+1;
}
ans = max(ans,dis(tb[i], tb[x]));
ans = max(ans,dis(tb[i+1], tb[x]));
}
printf("%lld\n",ans);
}
}
解释版:
const int maxn = 1e5 + 5;
struct point {
int x, y;
friend int operator * (const point& a, const point& b){
return a.x*b.y - a.y*b.x;
}
bool operator < (const point &_) const {
if(_.x == x) return y < _.y;
return x < _.x;
}
}tb[maxn], po[maxn];
point xl(const point& a, const point& b) {
return point{b.x-a.x, b.y-a.y};
}
ll dis(point a, point b) {
return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}
void solve() {
int n;
while(~scanf("%d", &n)){
for(int i = 1 ; i <= n ; i ++) {
scanf("%d%d",&po[i].x, &po[i].y);
}
sort(po+1, po+n+1);
int k = 0;
for(int i = 1 ; i <= n ; i ++) {
while(k > 1 && xl(tb[k-1], po[i]) * xl(tb[k-1], tb[k]) >= 0) --k;
tb[++k] = po[i];
}
int tmp = k;
for(int i = n - 1 ; i >= 1 ; i --) {
while(k > tmp && xl(tb[k-1], po[i]) * xl(tb[k-1], tb[k]) >= 0) --k;
tb[++k] = po[i];
}
// 因为凸包是一条线的特殊情况, 所以不能要共线的点, 所以加个=, 去掉那些点.
if (n > 1) -- k; //首先看清楚我的凸包是以逆时针给出的.(并且去了共线的点),这样我在判定
//三角形面积的时候, 叉积是正的, 这样我就可以直接是当前值小于后面哪一个的值. 而如果
//你是以顺时针给出来的凸包, 则你会发现叉积出来是负的(随便画一个图就可以知道)但是面积
//比较是正的,所以你的比较符号要相反. 还有就是去不去共线的点也会相应导致下面的比较
//符号取不取等于. 总之记得方向会影响很多, 不懂就把图画出来看看.
int x = 3; tb[k+1] = tb[1]; //防止越界.
ll ans = 0; //循环找最大面积的三角形.
for(int i = 1 ; i <= k ; i++) {
while(xl(tb[x], tb[i]) * xl(tb[x], tb[i+1]) <
xl(tb[x%k+1], tb[i]) * xl(tb[x%k+1], tb[i+1])){
x = x%k+1; // 相当于x++, 然后x = (x-1)%k+!; 是一个循环的.
}
ans = max(ans,dis(tb[i], tb[x])); //取两条向量中较长的那条.
ans = max(ans,dis(tb[i+1], tb[x]));
}
printf("%lld\n",ans); //这个就是最后旋转卡壳的最后答案, 求出最远的了.
}
}
可求平面中任意形状的多边形面积… 前提是给出的逆时针方向的点, 如果是顺时针还要记得正负, 方法就是选定一个起点, 其他点都跟起点连一条线, 那么连续的两个点之间都是一个三角形的面积, 注意不要加abs, 因为这个是有向面积, 如果是负的, 一定后面有一部分正的面积可以将其抵消掉.
void solve() {
// 首先给定点是逆时针, 那么按照顺序叉乘就是正的!
int n;
while(cin >> n && n) {
for (int i = 1 ; i <= n ; i ++) {
cin >> po[i].x >> po[i].y;
}
db ans = 0;
for (int i = 2 ; i < n ; i ++) {
ans += 1.0 * (xl(po[1], po[i]) * xl(po[1], po[i+1])) / 2;
}
printf("%.1f\n", ans);
}
}
ll ans = 0; // K为凸包点的数量
for (int i = 1 ; i <= k ; i ++) {
int t = i%k+1;
int g = t%k+1;
while(t != i && g != t) {
ans = max(ans, xl(tb[i], tb[t]) * xl(tb[i], tb[g]));
while(xl(tb[i], tb[t]) * xl(tb[i], tb[g]) <
xl(tb[i], tb[t]) * xl(tb[i], tb[g%k+1]))
g = g%k+1;
t = t%k+1;
}
}
printf("%.2f\n", 0.5*ans);