凸包 (旋转卡壳)

凸包板子:

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);

你可能感兴趣的:(板子,凸包/旋转卡壳/点对)