https://www.acmicpc.net/problem/3878
1. n의 점 집합에 대해 볼록 껍질을 만든다.
2. m의 점 집합에 대해 볼록 껍질을 만든다.
3. 두 볼록 껍질이 교차하는지 검사한다. 교차하면 NO. 교차안하면 YES다.
종만북에서 소스를 가져왔다.
#include<cstring> #include<algorithm> #include<cstdio> #include<cassert> #include<cmath> #include<iostream> #include<string> #include<vector> using namespace std; const double EPSILON = 1e-8; const double PI = 2.0 * acos(0.0); // 2차원 벡터를 표현한다 struct vector2 { double x, y; explicit vector2(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} // 두 벡터의 비교 bool operator == (const vector2& rhs) const { return x == rhs.x && y == rhs.y; } bool operator < (const vector2& rhs) const { if(x != rhs.x) return x < rhs.x; return y < rhs.y; } // 벡터의 덧셈과 뺄셈 vector2 operator + (const vector2& rhs) const { return vector2(x + rhs.x, y + rhs.y); } vector2 operator - (const vector2& rhs) const { return vector2(x - rhs.x, y - rhs.y); } vector2 operator - () const { return vector2(-x, -y); } // 스칼라로 곱셈 vector2 operator * (double rhs) const { return vector2(x * rhs, y * rhs); } // 벡터의 길이를 반환한다 double norm() const { return hypot(x, y); } // 방향이 같은 단위 벡터 (unit vector) 를 반환한다 vector2 normalize() const { double n = norm(); return vector2(x / n, y / n); } // x축의 양의 방향으로부터 이 벡터까지 반시계방향으로 잰 각도 double polar() const { return fmod(atan2(y, x) + 2*PI, 2*PI); } // 내적/외적의 구현 double dot(const vector2& rhs) const { return x * rhs.x + y * rhs.y; } double cross(const vector2& rhs) const { return x * rhs.y - rhs.x * y; } // 이 벡터를 rhs 에 사영한 결과 vector2 project(const vector2& rhs) const { vector2 r = rhs.normalize(); return r * r.dot(*this); } }; // 원점에서 벡터 b 가 벡터 a 의 반시계 방향이면 양수, 시계 방향이면 음수, 평행이면 0 을 반환한다 double ccw(vector2 a, vector2 b) { return a.cross(b); } // 점 p 를 기준으로 벡터 b 가 벡터 a 의 반시계 방향이면 양수, 시계 방향이면 음수, 평행이면 0 을 반환한다 double ccw(vector2 p, vector2 a, vector2 b) { return ccw(a-p, b-p); } // 두 구간 [a,b], [c,d] 가 교집합이 없는지 여부를 확인한다 double disjoint(double a, double b, double c, double d) { if(a > b) swap(a, b); if(c > d) swap(c, d); return b < c || d < a; } // 두 선분이 서로 접촉하는지 여부를 반환한다 bool segmentIntersects(vector2 a, vector2 b, vector2 c, vector2 d) { double ab = ccw(a, b, c) * ccw(a, b, d); double cd = ccw(c, d, a) * ccw(c, d, b); // 두 선분이 한 직선 위에 있는 경우 if(ab == 0 && cd == 0) return !disjoint(a.x, b.x, c.x, d.x) && !disjoint(a.y, b.y, c.y, d.y); return ab <= 0 && cd <= 0; } // 점 q 가 다각형 p 안에 포함되어 있을 경우 참, 아닌 경우 거짓을 반환한다. // q 가 다각형의 경계 위에 있는 경우의 반환값은 정의되어 있지 않다. bool isInside(vector2 q, const vector<vector2>& p) { int crosses = 0; for(int i = 0; i < p.size(); ++i) { int j = (i + 1) % p.size(); // (p[i], p[j]) 가 반직선을 세로로 가로지르는가? if((p[i].y > q.y) != (p[j].y > q.y)) { double atX = (p[j].x - p[i].x) * (q.y - p[i].y) / (p[j].y - p[i].y) + p[i].x; if(q.x < atX) ++crosses; } } return crosses % 2 > 0; } typedef vector<vector2> polygon; // points 에 있는 점들을 모두 포함하는 최소의 볼록 다각형을 찾는다 polygon giftWrap(const vector<vector2>& points) { int n = points.size(); polygon hull; // 가장 왼쪽 아래 점을 찾는다. 이 점은 껍질에 반드시 포함된다. vector2 pivot = *min_element(points.begin(), points.end()); hull.push_back(pivot); while(true) { // (p - ph) 가 가장 왼쪽인 점을 찾는다. 평행인 점이 여러 개 있으면 // 가장 먼 것을 선택한다. vector2 ph = hull.back(), next = points[0]; for(int i = 1; i < n; i++) { double cross = (next - ph).cross(points[i] - ph); double dist = (next - ph).norm() - (points[i] - ph).norm(); if(cross > 0 || (cross == 0 && dist < 0)) next = points[i]; } // 시작점으로 돌아왔으면 종료한다. if(next == pivot) break; hull.push_back(next); } return hull; } // 두 다각형이 서로 닿거나 겹치는지 여부를 반환한다. // 한 점이라도 겹친다면 true 를 반환한다. bool polygonIntersects(const polygon& p, const polygon& q) { int n = p.size(), m = q.size(); // 우선 한 다각형이 다른 다각형에 포함되어 있는 경우를 확인하자 if(isInside(p[0], q) || isInside(q[0], p)) return true; // 이외의 경우, 두 다각형이 서로 겹친다면 서로 닿는 두 변이 반드시 존재한다 for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) if(segmentIntersects(p[i], p[(i+1)%n], q[j], q[(j+1)%m])) return true; return false; } int main(int argc, char* argv[]) { int cases; scanf("%d", &cases); while(cases--) { vector<vector2> inputs[2]; int n, m; int x, y; scanf("%d %d", &n, &m); for(int i = 0; i < n; i++) { scanf("%d %d", &x, &y); inputs[0].push_back(vector2(x, y)); } for(int i = 0; i < m; i++) { scanf("%d %d", &x, &y); inputs[1].push_back(vector2(x, y)); } polygon hulls[2]; for(int i = 0; i < 2; i++) hulls[i] = giftWrap(inputs[i]); if(polygonIntersects(hulls[0], hulls[1])) printf("NO\n"); else printf("YES\n"); } }
https://www.acmicpc.net/problem/2254
누워있다가 갑자기 아이디어가 떠올라서 풀었다.
1. 전체 좌표에 대해 볼록 껍질을 만든다.(가장 큰 볼록껍질인셈임)
2. 가장 큰 볼록껍질안에 감옥좌표가 들어가면 겹수++를 해주고 볼록껍질의 점들을 다 지운다.
3. 저장된 좌표가 0이면 break로 나와주고 좌표가 0이 아닐경우 1번으로 돌아간다.
4. 겹수를 출력한다.
1. 초기 상태다. 빨간점이 감옥이고 나머지 점들로 잘 감싼다하면 아래처럼 그려질수있다.
2. 아주 바깥쪽의 볼록껍질을 제거하면 아래처럼 된다.
3. 이런식으로 볼록껍질을 하나씩 없애주면서 울타리 +1 해주면 된다.
#include <iostream> #include <vector> #include <cmath> #include <algorithm> using namespace std; const double PI = 2.0 * acos(0.0); struct vector2 { double x, y; vector2(double x_ = 0, double y_ = 0) : x(x_), y(y_) {} // 두 벡터가 같은 경우 bool operator == (const vector2& rhs) const { return x == rhs.x && y == rhs.y; } // 벡터의 덧셈과 뺄셈, 단항 연산자의 구현 vector2 operator + (const vector2& rhs) const { return vector2(x + rhs.x, y + rhs.y); } vector2 operator - (const vector2& rhs) const { return vector2(x - rhs.x, y - rhs.y); } vector2 operator - () const { return vector2(-x, -y); } bool operator < (const vector2& rhs) const { return x != rhs.x? x < rhs.x: y < rhs.y; } // 스칼라로 곱셈 vector2 operator * (double rhs) const { return vector2(x * rhs, y * rhs); } // 벡터의 길이를 반환한다 double norm() const { return hypot(x, y); } // 방향이 같은 단위 벡터 (unit vector) 를 반환한다 vector2 normalize() const { double n = norm(); return vector2(x / n, y / n); } // 내적/외적의 구현 double dot(const vector2& rhs) const { return x * rhs.x + y * rhs.y; } double cross(const vector2& rhs) const { return x * rhs.y - rhs.x * y; } // 이 벡터를 rhs 에 사영한 결과 vector2 project(const vector2& rhs) const { vector2 r = rhs.normalize(); return r * r.dot(*this); } }; double ccw(vector2 a, vector2 b) { return a.cross(b); } double ccw(vector2 p, vector2 a, vector2 b) { return ccw(a-p, b-p); } typedef vector<vector2> polygon; polygon giftWrap(const vector<vector2>& points) { int n = points.size(); polygon hull; vector2 pivot = *min_element(points.begin(), points.end()); hull.push_back(pivot); while(true) { vector2 ph = hull.back(), next = points[0]; for(int i=1;i<n;i++) { double cross = ccw(ph, next, points[i]); double dist = (next - ph).norm() - (points[i] - ph).norm(); if(cross > 0 || (cross == 0 && dist < 0)) next = points[i]; } if(next==pivot) break; hull.push_back(next); } return hull; } bool isInside(vector2 q, const vector<vector2>& p) { int crosses = 0; for(int i=0;i<p.size();i++) { int j = (i+1)%p.size(); if((p[i].y > q.y) != (p[j].y > q.y)) { double atX = (p[j].x - p[i].x) * (q.y - p[i].y) / (p[j].y - p[i].y) + p[i].x; if(q.x < atX) ++crosses; } } return crosses % 2 > 0; } int main() { vector<vector2> vec; int n,ox,oy; cin>>n>>ox>>oy; for(int i=0;i<n;i++) { int x,y; cin>>x>>y; vec.push_back(vector2(x,y)); } int cnt = 0; while(1) { vector<vector2> v; vector<vector2> ret = giftWrap(vec); //볼록 껍질을 구한다. if(isInside(vector2(ox,oy),ret)) //볼록 껍질 안에 감옥이 있으면 { cnt++; //겹 수를 1 증가시키고 for(int i=0;i<ret.size();i++) //전체 기둥에서 볼록 껍질의 좌표들을 제거한다. vec.erase(remove(vec.begin(), vec.end(), ret[i]), vec.end()); } else break; if(vec.size()==0) //없으면 break break; } cout<<cnt<<"\n"; return 0; }
https://www.acmicpc.net/problem/2166
위 소스의 함수들을 다 가져온다. area함수로 면적을 계산해준다. 메인 함수만..
int main() { vector<vector2> vec; int n; scanf("%d",&n); for(int i=0;i<n;i++) { int x,y; scanf("%d %d",&x,&y); vec.push_back(vector2(x,y)); } printf("%.1f\n",area(vec)); return 0; }
'algorithm > problem solving' 카테고리의 다른 글
acmicpc.net 10164(DP), 2568(LIS, DP), 11054(LIS, DP) (0) | 2016.11.04 |
---|---|
acmicpc.net 1535(배낭 문제, DP), 1915(DP), 1562(비트마스크, DP) (0) | 2016.11.01 |
acmicpc.net 12781(벡터, 기하), 1708(볼록 껍질, 기하), 9240(볼록 껍질, 기하) (0) | 2016.10.29 |
acmicpc.net 9934(트리, BFS), 5639(이진 트리), 6597(트리) (0) | 2016.10.28 |
acmicpc.net 1967,1167(트리,DFS), 11725(트리,DFS), 1068(트리,DFS) (0) | 2016.10.27 |