题目描述
在一个二维的花园中,有一些用 (x, y) 坐标表示的树。由于安装费用十分昂贵,你的任务是先用最短的绳子围起所有的树。只有当所有的树都被绳子包围时,花园才能围好栅栏。你需要找到正好位于栅栏边界上的树的坐标。
示例1:
输入: [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]]
输出: [[1,1],[2,0],[4,2],[3,3],[2,4]]
解释:示例 2:
输入: [[1,2],[2,2],[4,2]]
输出: [[1,2],[2,2],[4,2]]
解释:即使树都在一条直线上,你也需要先用绳子包围它们。
注意:
所有的树应当被围在一起。你不能剪断绳子来包围树或者把树分成一组以上。
输入的整数在 0 到 100 之间。
花园至少有一棵树。
所有树的坐标都是不同的。
输入的点没有顺序。输出顺序也没有要求。
Andrew 算法代码
// b[0]-a[0], b[1]-a[1]
// c[0]-b[0], c[1]-b[1]
class Solution {
public:
int cross(vector<int>& a, vector<int>& b, vector<int>& c) {
return (b[0]-a[0])*(c[1]-b[1]) - (c[0]-b[0])*(b[1]-a[1]);
}
vector<vector<int>> outerTrees(vector<vector<int>>& trees) {
int n = trees.size();
if(n < 4) {
return trees;
}
// 排序 先x轴由小到大,在y轴由小到大
sort(trees.begin(), trees.end(),[&](vector<int>& a, vector<int>& b) {
if(a[0] == b[0]) {
return a[1] < b[1];
}
return a[0] < b[0];
});
vector<int> visit(n, false);
vector<int> st;
st.push_back(0);
// 求下凸
for(int i = 1; i < n; ++i) {
while(st.size()>1 && cross(trees[st[st.size()-2]], trees[st.back()], trees[i]) < 0) {
visit[st.back()] = false;
st.pop_back();
}
visit[i] = true;
st.push_back(i);
}
// 求上凸
int m = st.size();
for(int i = n-2; i >= 0; --i) {
if(!visit[i]) {
while(st.size() > m && cross(trees[st[st.size()-2]], trees[st.back()], trees[i]) < 0) {
visit[st.back()] = false;
st.pop_back();
}
visit[i] = true;
st.push_back(i);
}
}
// 0 放进去两次
st.pop_back();
vector<vector<int>> res;
for(auto & v : st) {
res.emplace_back(trees[v]);
}
return res;
}
};