Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

15-yuyu0830 #60

Merged
merged 2 commits into from
Jul 4, 2024
Merged

15-yuyu0830 #60

merged 2 commits into from
Jul 4, 2024

Conversation

yuyu0830
Copy link
Collaborator

@yuyu0830 yuyu0830 commented May 31, 2024

🔗 문제 링크

다각형의 면적

✔️ 소요된 시간

5시간

✨ 수도 코드

❓ 문제

n각형의 꼭지점 n개의 좌표 (x, y) 가 다각형을 이루는 순서대로 주어진다. 이 다각형의 면적을 구하자

  • -100,000 <= x, y <= 100,000
  • 3 <= n <= 10,000

❗ 풀이

image
요즘 푸는 문제마다 왜 다 이 꼴인지 모르겠다..

1️⃣ 첫번째 풀이

다각형을 포함하는 x, y축에 평행한 가장 작은 직사각형을 그린 뒤, 다각형에 포함되지 않는 범위를 하나씩 빼나가는 방식이다.

즉 아래의 파란 다각형에 대해 주황색 부분을 빼서 전체의 넓이를 구하는 방식이다.
image

다각형의 한 변을 포함하며 다각형의 내부에 포함되지 않는 삼각형(초록색)을 그려 변의 개수 만큼 빼주는 방식이다.

이미지 예제

image
image
image
image

그렇게 바깥 삼각형을 모두 빼고 나면 다각형의 넓이만 남게 된다는 풀이였다!

여기서 어려웠던 점은 다각형의 각 변큰 사각형의 어느 꼭지점 을 연결해야하는지 판단해야하는 부분이였다.

이 부분은 모든 꼭지점을 x 기준으로 정렬한 뒤 각 꼭지점을 시계방향으로 순회하는데, 탐색하는 꼭지점이 사각형의 각 변에 닿으면 다음 꼭지점과 이어주는 방식으로 구현했다.

이미지 예제

image
image
image
image

소스코드

#include <iostream>
#include <vector>
#include <algorithm>
#include <math.h>

#define MAX 99999999
#define MIN -99999999

using namespace std;

struct Point {
    int x, y;
    Point(int X, int Y) : x(X), y(Y) {};
    Point() {};

    bool operator== (const Point &p) {
        return x == p.x && y == p.y;
    }
};

bool compare(const Point& a, const Point& b) {
    if (a.x == b.x) return a.y < b.y;
    return a.x < b.x;
}

float getArea(Point a, Point b) {
    float x = (float) abs(a.x - b.x);
    float y = (float) abs(a.y - b.y);

    return x * y;
}

float getTriangleArea(Point a, Point b, Point c) {
    int mx = min(min(a.x, b.x), c.x);
    int my = min(min(a.y, b.y), c.y);
    int Mx = max(max(a.x, b.x), c.x);
    int My = max(max(a.y, b.y), c.y);

    float area = (float) ((Mx - mx) * (My - my));

    return area - ((getArea(a, b) + getArea(b, c) + getArea(a, c)) / 2);
}

bool isin(int t, int a, int b) {
    return t >= a && t <= b;
}

vector<Point> v;
Point vp[2][2];

int minX = MAX, maxX = MIN;
int minY = MAX, maxY = MIN;

int main() {
    int n; cin >> n;

    for (int i = 0; i < n; i++) {
        int a, b; cin >> a >> b;

        v.push_back(Point(a, b));

        minX = min(minX, a);
        maxX = max(maxX, a);
        minY = min(minY, b);
        maxY = max(maxY, b);
    }

    vp[0][0] = Point(minX, minY);
    vp[0][1] = Point(minX, maxY);
    vp[1][0] = Point(maxX, minY);
    vp[1][1] = Point(maxX, maxY);

    sort(v.begin(), v.end(), compare);

    float area = getArea(vp[0][0], vp[1][1]);

    int cnt = 1, cur = 0;
    bool dir = true, big = true;

    while (cnt != -1) {
        if ((big && isin(v[cnt].y, v[cur].y, dir ? MAX : v[0].y)) || (!big && isin(v[cnt].y, dir ? v[n - 1].y : MIN, v[cur].y))) {
            area -= getTriangleArea(v[cur], v[cnt], vp[!big][dir]);
            cur = cnt;

            if ((big && v[cnt].y == maxY) || (!big && v[cnt].y == minY)) big = !big;
        }
        
        if (cnt == v.size() - 1) {
            dir = false;
            cur = cnt;
        }

        cnt += dir ? 1 : -1;
    }

    printf("%.1f\n", round(area * 10) / 10);
}

열심히 적긴 했는데 위 풀이는 틀렸다. 다각형이 오목한 다각형도 존재하기 때문이다...!

오류 예시

image
image
image

위 노란색 사각형 같은 오류가 생긴다..!

3시간동안 열심히 짠 로직이지만 다시 생각할 필요가 생겼다..

2️⃣ 두번째 풀이

얼마전에 친구가 폐곡선 면적을 구할 때 도형의 윗 부분에 대한 적분 에서 아랫부분에 대한 적분 을 빼면 폐곡선의 면적을 구할 수 있다고 한 것에서 아이디어를 얻었다.

image
image

우선 계산을 단순하게 하기 위해서 다각형의 좌하단을 원점으로 삼았다.

image

이후 각 변마다 적분을 하는데, 여기서 꼭지점의 x의 진행 방향에 따라 더할지 말지 결정했다.

이미지 예시

image
image
image
image
image
image

위의 방식으로 푸는데 성공했다...!

소스코드

#include <iostream>
#include <vector>
#define MAX 99999999

using namespace std;

typedef long long ll;

struct Point {
    ll x, y;
    Point(ll X, ll Y) : x(X), y(Y) {};
};

ll getArea(Point a, Point b) {
    ll square = abs((a.x - b.x) * min(abs(a.y), abs(b.y))) * 2;
    ll triangle = abs((a.x - b.x) * (a.y - b.y));

    return square + triangle;
}

vector<Point> v;

int main() {
    ll a, b, area = 0, mx = MAX, my = MAX;

    int n; cin >> n;

    for (int i = 0; i < n; i++) {
        cin >> a >> b;
        v.push_back(Point(a, b));

        mx = min(mx, a);
        my = min(my, b);
    }

    for (auto &p : v) {
        p.x -= mx;
        p.y -= my;
    }

    v.push_back(v[0]);

    for (int i = 0; i < n; i++) {
        Point cur = v[i];
        Point next = v[i + 1];

        area += getArea(cur, next) * (cur.x <= next.x ? 1 : -1);
    }

    printf("%.1f\n", abs((double) area / 2));
}

📚 새롭게 알게된 내용

수학 문제 재밌는데 난이도 진짜... 그래도 직접 풀었다는 것에 의의를 둔다.
원점 안옮기고 풀 수도 있던데 어떻게 하는지 모르겠다.

Copy link
Collaborator

@InSange InSange left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수학은 진짜 알아둬야하는데 어려워서 안하는 ^^;

원점으로 옮기면 편하긴하지만 기준점을 어디로 잡느냐에 따라 다른것 같네요
가장 낮은 위치의 점을 바탕으로하면 그 값을 기준으로 밑변의 좌표가 되지 않을까요? 그럼 원점대신 해당 점을 기준으로 사각형, 삼각형 면적을 그려주면 되니까 인것 같습니다.
하지만 그래도 원점이 이해하기 더 편하고 직관적이다능...

저도 문제를 보자마자 첫번째 방식을 떠올렸는데요. 대신 큰 사각형을 만들어서 하기보다는 해당 다각형의 중점을 구해줍니다.
N각형은 N개의 변이 만들어 질 것이고 두 점을 순차적으로 잘 연결하여 각 변에서 중점까지 선분을 그어주면 삼각형으로 계산할 수 있지 않을까요..?
중점의 선분은 삼각형의 높이가 되고 두 꼭짓점의 선분은 밑변이 되니까 N개의 삼각형이 만들어지고 합을 구해준다면..?

@yuyu0830
Copy link
Collaborator Author

yuyu0830 commented Jul 1, 2024

저도 문제를 보자마자 첫번째 방식을 떠올렸는데요. 대신 큰 사각형을 만들어서 하기보다는 해당 다각형의 중점을 구해줍니다.
N각형은 N개의 변이 만들어 질 것이고 두 점을 순차적으로 잘 연결하여 각 변에서 중점까지 선분을 그어주면 삼각형으로 계산할 수 있지 않을까요..?
중점의 선분은 삼각형의 높이가 되고 두 꼭짓점의 선분은 밑변이 되니까 N개의 삼각형이 만들어지고 합을 구해준다면..?

그 방식도 생각 못해본건 아닌데 오목한 다각형의 경우에 중점이 도형 밖에 있기도 하고 계산한 삼각형의 범위가 기존 삼각형에 포함되지 않는 경우도 있어서 포기했습니다.. '오류 예시' 탭에 도형으로 생각해보시면 될 것 같아요 ㅠ

Copy link
Collaborator

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다각형을 포함하는 제일 작은 직사각형을 구해서 빼려고 했는데 신기하게도 틀린 풀이에 있었습니다...오목한 다각형도 있고 생각해보니 구할 때 너무 노가다 일 것 같네요. 삼각형과 사각형을 이용해서 적분으로 계산하는건 생각도 못했는데 좋은 풀이 잘 봤습니다.

@yuyu0830 yuyu0830 merged commit 5600fa0 into main Jul 4, 2024
@yuyu0830 yuyu0830 deleted the 15-yuyu0830 branch July 4, 2024 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants