P4557 [JSOI2018]战争 凸包的闵可夫斯基和

https://www.luogu.com.cn/problem/P4557

  • 题目描述很简单,给定两个凸包,我们设为 a , b a,b a,b,现在问给 b b b凸包一个整体偏移,偏移量为 k → \overrightarrow{k} k ,给定 k → = ( x , y ) \overrightarrow{k}=(x,y) k =(x,y),问这两个凸包有没有公共部分
  • 换句话说,就是问给定的 k → \overrightarrow{k} k 是否满足 b + k → = a b+\overrightarrow{k}=a b+k =a,即 k → = a − b \overrightarrow{k}=a-b k =ab

首先介绍一下闵可夫斯基和
定义:两个欧几里得空间点集的和,也称作两个空间的膨胀集,点集 A A A B B B的闵可夫斯基和定义为 A + B = { a + b , a ∈ A , b ∈ B } A+B=\{a+b,a\in A,b\in B\} A+B={a+b,aA,bB}

  • 求取方法是把这两个空间内部所有的向量按照极角排序,然后找到一个起点,按照极角从小到大依次放边,最后构成的形状就是两个空间的闵可夫斯基和

那么这道题我们其实要求的是 a a a − b -b b的闵可夫斯基和,只要 k → \overrightarrow{k} k 终点在两个空间的膨胀集内部,或者边界,就说明会发生冲突,否则不会

  • 什么叫做凸集?凸集的几何意义是如果一个集合任意两个元素连线上的点也在集合内部,那么这个集合就是一个凸集,如下图,左侧是一个凸集但是右侧却不是一个凸集;代数定义是对于 S S S的子集内的所有 x x x y y y,并且在区间 [ 0 , 1 ] [0,1] [0,1]内所有的值 t t t,如果点 ( 1 − t ) x + t y (1-t)x+ty (1t)x+ty也属于 S S S,那么 S S S就是一个凸集
    P4557 [JSOI2018]战争 凸包的闵可夫斯基和_第1张图片
  • 那么这里产生了一个疑问,为什么两个凸集的闵可夫斯基和仍然是一个凸集?我们可以进行如下证明,设 A , B A,B A,B是两个凸集,设 C = A + B , α 1 , α 2 ∈ A , β 1 , β 2 ∈ B C=A+B,\alpha_1,\alpha_2\in A,\beta_1,\beta_2\in B C=A+B,α1,α2A,β1,β2B
  • 由于 ( 1 − t ) α 1 + t α 2 ∈ A (1-t)\alpha_1+t\alpha_2\in A (1t)α1+tα2A ( 1 − t ) β 1 + t β 2 ∈ B (1-t)\beta_1+t\beta_2\in B (1t)β1+tβ2B,所以根据凸集和的定义有 ( 1 − t ) α 1 + t α 2 + ( 1 − t ) β 1 + t β 2 ∈ C (1-t)\alpha_1+t\alpha_2+(1-t)\beta_1+t\beta_2\in C (1t)α1+tα2+(1t)β1+tβ2C
  • 所以 ( 1 − t ) ( α 1 + β 1 ) + t ( α 2 + β 2 ) ∈ C (1-t)(\alpha_1+\beta_1)+t(\alpha_2+\beta_2)\in C (1t)(α1+β1)+t(α2+β2)C又因为根据凸集和的定义有 α 1 + β 1 ∈ C , α 2 + β 2 ∈ C \alpha_1+\beta_1\in C,\alpha_2+\beta_2\in C α1+β1C,α2+β2C,这就证明了C是一个凸集

接下来回到这道题,梳理一下思路,我们首先要求出 A A A − B -B B两个凸集的闵可夫斯基和,这个结果是一个凸包,求闵可夫斯基和我们可以使用固定一个初始点然后进行归并,始终把外面的线加进去,这里使用向量叉积来判断线是否在外部, − B -B B的意思就是坐标都取相反数,算出来要再跑一次 A n d r e w Andrew Andrew防止出现共线,然后进行点是否在凸包内部的二分判定,程序如下

#include 

using namespace std;
#define db double
const db eps = 1e-10;
const int MAXN = 1e5 + 100;
int sgn(db x){
    if(fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}
struct Point{
    db x, y;
    Point(){}
    Point(db x, db y): x(x), y(y){}
    Point operator + (const Point &B)const{
        return Point(x + B.x, y + B.y);
    }
    Point operator - (const Point &B)const{
        return Point(x - B.x, y - B.y);
    }
    bool operator < (const Point &B)const{
        return sgn(x - B.x) < 0 || (sgn(x - B.x) == 0 && sgn(y - B.y) < 0);
    }
    bool operator == (const Point &B)const{
        return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;
    }
}s1[MAXN], s2[MAXN], ch1[MAXN], ch2[MAXN];
Point M[MAXN], ans[MAXN];
typedef Point Vector;
db Cross(Vector A, Vector B){
    return A.x * B.y - A.y * B.x;
}
int Convex_hull(Point *s, Point *ch, int n){
    int v = 0;
    sort(s, s + n);
    n = unique(s, s + n) - s;
    for(int i=0;i<n;i++){
        while(v > 1 && sgn(Cross(ch[v - 1] - ch[v - 2], s[i] - ch[v - 2])) <= 0){
            v -= 1;
        }
        ch[v++] = s[i];
    }
    int j = v;
    for(int i=n-2;i>=0;i--){
        while(v > j && sgn(Cross(ch[v - 1] - ch[v - 2], s[i] - ch[v - 2])) <= 0){
            v -= 1;
        }
        ch[v++] = s[i];
    }
    if(n > 1) v -= 1;
    return v;
}
int check(Point A, Point *ch, int n){
    int l = 0;
    int r = n - 1;
    while(r - l > 1){
        int mid = ((r - l) >> 1) + l;
        db a1 = Cross(ch[mid] - ch[0], A - ch[0]);
        db a2 = Cross(ch[mid + 1] - ch[0], A - ch[0]);
        if(sgn(a1) >= 0 && sgn(a2) <= 0){
            if(sgn(Cross(ch[mid + 1] - ch[mid], A - ch[mid])) >= 0) return 1;
            return 0;
        }else if(sgn(a1) < 0){
            r = mid;
        }else{
            l = mid;
        }
    }
    return 0;
}
int Minkowski_sum(int n, int m){
    int tot;
    for(int i=1;i<n;i++){
        s1[i] = ch1[i] - ch1[i - 1];
    }
    s1[n] = ch1[0] - ch1[n - 1];
    for(int i=1;i<m;i++){
        s2[i] = ch2[i] - ch2[i - 1];
    }
    s2[m] = ch2[0] - ch2[m - 1];
    ans[tot = 0] = s1[0] + s2[0];
    int pt1, pt2;
    pt1 = pt2 = 1;
    while(pt1 <= n && pt2 <= m){
        tot += 1;
        ans[tot] = ans[tot - 1] + (sgn(Cross(s1[pt1], s2[pt2])) >= 0 ? s1[pt1++] : s2[pt2++]);
    }
    while(pt1 <= n){
        tot += 1;
        ans[tot] = ans[tot - 1] + s1[pt1++];
    }
    while(pt2 <= m){
        tot += 1;
        ans[tot] = ans[tot - 1] + s2[pt2++];
    }
    return tot;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, m, q;
    db x, y;
    cin >> n >> m >> q;
    for(int i=0;i<n;i++){
        cin >> s1[i].x >> s1[i].y;
    }
    int a = Convex_hull(s1, ch1, n);
    for(int i=0;i<m;i++){
        cin >> s2[i].x >> s2[i].y;
        s2[i].x = -s2[i].x;
        s2[i].y = -s2[i].y;
    }
    int b = Convex_hull(s2, ch2, m);
    int c = Minkowski_sum(a, b);
    c = Convex_hull(ans, M, c);
    while(q--){
        cin >> x >> y;
        cout << check(Point(x, y), M, c) << '\n';
    }
    return 0;
}

你可能感兴趣的:(#,计算几何,算法)