587. 安装栅栏 求凸包 Andrew's Algorithm

先按x从小到大,如果x相同,则y从小到大的顺序排序

已知x最小、y最小、x最大、y最大的点肯定为凸包上的点,为什么,可以仔细想想

因此,排序后的第一个肯定为凸包上的点

分两部分求

先求凸包的下半部分,再求上半部分

根据规则排序后,凸包下半部分的顶点,在数组中肯定是逆时针的顺序

先选数组中前两个点A、B放到凸包集合中,接下来遍历数组中剩下的每个点,

如第三个点C,判断,如果向量AB * 向量AC < 0,则说明点B在线段AC的上面,即C可能是一个凸包点,而B不是

因此,从凸包集合中剔除B,加入C

如果 = 0,则说明共线,点C在直线AB上,直接加入凸包集合即可

否则,continue

想一下求余弦的公式就知道为啥了

当然如果选C时凸包中的点数大于2,比如有X、A、B,则如果B被剔除,则还要考虑A是否合适

举个栗子:

由上图可知,必须得判断A是否合适了吧

凸包上半部分的选点,数组从后向前遍历,这样就相当于x从大到小,如果x相同则y从大到小排序,保证了数组中上半部分凸包顶点的逆时针性质

代码如下

class Solution {
public:

    int xmul(vector<int> a, vector<int> b, vector<int> c)
    {
        return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
    }
    bool vis[110][110];
    vector<vector<int>> outerTrees(vector<vector<int>>& trees) {

        vector<vector<int>> v;
        memset(vis, 0, sizeof(vis));
        sort(trees.begin(), trees.end());
        int n = trees.size();
        for(int i = 0; i < n; i++)
        {
            while(v.size() >= 2 && xmul(v[v.size() - 2], v[v.size() - 1], trees[i]) < 0)
                vis[v[v.size() - 1][0]][v[v.size() -1][1]] =  0, v.pop_back();
            v.push_back(trees[i]);
            vis[trees[i][0]][trees[i][1]] = 1;
        }
        int k = v.size();
        for(int i = n - 2; i >= 0; i--)
        {
            while(v.size() >= k + 1 && xmul(v[v.size() - 2], v[v.size() - 1], trees[i]) < 0)
                v.pop_back();
            if(vis[trees[i][0]][trees[i][1]] == 0)
                v.push_back(trees[i]);
        }

        return v;
    }
};
自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
原文地址:https://www.cnblogs.com/WTSRUVF/p/15510718.html