알고리즘/algospot

Algospot NERD2

꾸준함. 2018. 6. 28. 17:22

문제 링크입니다: https://algospot.com/judge/problem/read/NERD2


문제 수를 x축, 라면 그릇 수를 y축으로 지정하여 좌표평면상의 점들로 사람들을 표현하면 이해하기 쉬운 문제였습니다.

A라는 사람이 B라는 사람보다 푼 문제 수도 적고, 라면 그릇 수도 적다면 NERD가 아니므로 좌표평면에서 직사각형 내부에 점이 있으면 NERD가 아닙니다.

그림으로 설명하자면,

예제에서 주어진 참가자 4명에 대해 좌표평면 위의 점으로 표현했을 떄 노란색 안에 점이 있다면 NERD가 아니라는 것을 확신할 수 있습니다!

또한, 지배당하지 않는 점들은 좌상단부터 우하단까지 계단식 모양을 형성하고, 이러한 특성을 갖기 떄문에 점이 추가되었을 떄 지배당하는지 여부를 쉽게 파악할 수 있습니다!


좌표평면의 점이 노란색에 포함되지 않는다면 지배당하지 않는다고 말하고 지배당하지 않는 점들을 저장하는 가장 쉬운 방법은 배열을 이용하는 것입니다.

하지만, N은 최대 50,000이고 배열을 이용하면 시간복잡도가 O(N^2)이기 떄문에 시간초과가 발생합니다.

따라서, 이진탐색트리 역할을 해주는 stl map을 이용해줍니다!(map은 key와 value를 first와 second에 저장해줍니다)

map이 익숙하지 않으시다면 다음 링크를 참고하세요!(https://ko.cppreference.com/w/cpp/container/map, 특히 lower_bound 함수를 주목해주세요)


#include <iostream>

#include <map>

using namespace std;

 

//현재 다른 점에 지배당하지 않는 점들의 목록을 저장

//coords[x]=y

map<int, int> coords;

//새로운 점 (x, y)가 기존의 다른 점들에 지배당하는지 확인한다

bool isDominated(int x, int y)

{

        //x보다 오른쪽에 있는 점 중 가장 왼쪽에 있는 점을 찾는다

        map<int, int>::iterator it = coords.lower_bound(x);

        //그런 점이 없으면 (x, y)는 지배당하지 않는다

        if (it == coords.end())

                 return false;

        //이 점은 x보다 오른쪽에 있는 점 중 가장 위에 있는 점이므로

        //(x, y)가 어느 점에 지배되려면 이 점에도 지배되어야 한다

        return y < it->second;

}

 

//새로운 점(x, y)에 지배당하는 점들을 트리에서 지운다

void removeDominated(int x, int y)

{

        map<int, int>::iterator it = coords.lower_bound(x);

        //(x, y)보다 왼쪽에 있는 점이 없다!

        if (it == coords.begin())

                 return;

        --it;

        //반복문 불변식: it (x y)의 바로 왼쪽에 있는 점

        while (true)

        {

                 //(x, y) 바로 왼쪽에 오는 점을 찾는다

                 //it가 표시하는 점이 (x, y)에 지배되지 않는다면 곧장 종료

                 if (it->second > y)

                         break;

                 //이전 점이 더 없으므로 it만 지우고 종료한다

                 if (it == coords.begin())

                 {

                         coords.erase(it);

                         break;

                 }

                 //이전 점으로 이터레이터를 하나 옮겨 놓고 it를 지운다

                 else

                 {

                         map<int, int>::iterator jt = it;

                         --jt;

                         coords.erase(it);

                         it = jt;

                 }

        }

}

 

//새 점(x, y)가 추가되었을 때 coords를 갱신하고

//다른 점에 지배당하지 않는 점들의 개수를 반환한다

int registered(int x, int y)

{

        //(x, y)가 이미 지배당하는 경우에는 그냥 (x, y)를 버린다

        if (isDominated(x, y))

                 return coords.size();

        //기존에 있던 점 중 (x, y)에 지배당하는 점들을 지운다

        removeDominated(x, y);

        coords[x] = y;

        return coords.size();

}

 

int main(void)

{

        int test_case;

        cin >> test_case;

 

        for (int i = 0; i < test_case; i++)

        {

                 int N;

                 cin >> N;

 

                 coords.clear();

 

                 int result = 0;

                 for (int j = 0; j < N; j++)

                 {

                         int x, y;

                         cin >> x >> y;

 

                         result += registered(x, y);

                 }

                 cout << result << endl;

        }

        return 0;

}


개발환경:Visual Studio 2017


지적, 조언, 질문 환영입니다! 댓글 남겨주세요~


[참고] 프로그래밍 대회에서 배우는 알고리즘 문제해결전략



반응형

'알고리즘 > algospot' 카테고리의 다른 글

Algospot RUNNINGMEDIAN  (0) 2018.07.03
Algospot INSERTION  (0) 2018.06.30
Algospot FORTRESS  (0) 2018.06.26
Algospot TRAVERSAL  (0) 2018.06.26
Algospot HABIT  (0) 2018.06.25