BOJ 백준 10816 : 숫자 카드 2

Updated:

시작하면서

 이번 문제는 이분 탐색(Binary Search) 문제이다. 이전 문제인 숫자 카드 문제의 변형 버전이다.

BOJ 10816 - 숫자 카드 2

​

문제 이해

문제를 읽어보자. 먼저 이 문제에서 나오는 변수는 총 4가지이다.

  • N, M : 카드의 개수
  • card vector : A, B

​

문제 아이디어

 해당 문제의 이전 숫자 카드 문제에서 2가지 조건이 추가되었다.

  1. 시간 제한이 2초 -> 1초로 변경되었다.
  2. 각 수가 적힌 숫자 카드를 몇 개 가지고 있는지를 출력해야 한다.

​ 이 조건들을 맞추기 위해서 이전과 다르게 시간도 줄여야한다. 시간을 줄이는 키워드는 “벡터에 동일한 숫자가 있으면 제거해서 이분 탐색 시간을 줄인다” 이다.

​ 조건은 이전과 동일하다.

  1. 완전 탐색을 사용한다고 하면 최악의 경우인 500,000 * 500,000 = 250,000,000,000에서 시간 초과가 발생한다.

  2. 배열을 사용해 있는지 없는지의 여부를 검사한다고 하면 -10,000,000 ~ 10,000,000의 범위라서 메모리 초과가 발생한다.

    정정한다. 메모리 초과 발생 안 한다. 메모리 제한 : 256MB = 256,000,000 / 20,000,002 * 8 = 160,000,016

  3. 해시 테이블을 사용한다고 해도 최대 500,000이므로 메모리 초과가 발생한다.

    정정한다. 메모리 초과 발생 안 한다. 메모리 제한 : 256MB = 256,000,000 / 500,000 * 8 = 4,000,000

​ 그렇기 때문에 (이분 탐색으로 풀고 싶어서…) 사실 메모리가 넉넉해서 배열을 2000만개 잡으면 더 쉽고 빠르게 풀 수 있다. 하지만 이분 탐색을 공부해야하기도 하고, 메모리를 많이 사용하는 것은 별로 좋아하지 않으므로 이분 탐색으로 해결했다.

​ “벡터에 동일한 숫자가 있으면 제거해서 이분 탐색 시간을 줄인다”를 multiset을 사용했고, 인풋을 받은 후에 새로운 벡터에 숫자와 개수를 pair<int, int>의 형태로 담았다.

​

해결

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>

using namespace std;

int GetNumberCountExistInVector(const vector<pair<int, int>>& v, const int number)
{
    int minNumber = 0, maxNumber = 0, avgNumber = 0;

    maxNumber = v.size() - 1;
    avgNumber = (minNumber + maxNumber) * 0.5;

    while (minNumber <= maxNumber)
    {
        if (v[avgNumber].first == number)
            return v[avgNumber].second;

        if (v[avgNumber].first > number)
        {
            maxNumber = avgNumber - 1;
        }
        else
        {
            minNumber = avgNumber + 1;
        }

        avgNumber = (minNumber + maxNumber) * 0.5;
    }

    return 0;
}

int main()
{
    // sync off
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    // sync off

    int N, M;
    multiset<int> s;
    vector<pair<int,int>> firstCard;
    vector<int> secondCard;

    cin >> N;
    for (int i = 0; i < N; i++)
    {
        int card = 0;

        cin >> card;

        s.insert(card);
    }

    cin >> M;
    secondCard.resize(M);
    for (int i = 0; i < M; i++)
    {
        cin >> secondCard[i];
    }

    int prevNum = -10000001;

    // 1. 카드의 값과 개수를 받아둔 세트를 firstCard 벡터에 초기화해준다.
    for (multiset<int>::iterator iter = s.begin(); iter != s.end(); iter++)
    {
        if (prevNum == *iter)
            continue;

        prevNum = *iter;

        firstCard.push_back(make_pair(*iter, s.count(*iter)));
    }

    // 2. secondCard의 각 값을 기준으로 firstCard에서 이분탐색을 통해 값이 몇개 있는지 찾는다.
    for (int i = 0; i < secondCard.size(); i++)
    {
        cout << GetNumberCountExistInVector(firstCard, secondCard[i]) << " ";
    }

    return 0;
}

​

마치며

 다른 사람들의 코드를 보니 수행 시간이 매우 빨라서 놀랐는데, equl_range를 사용하거나 2000만개의 배열을 사용하는 방법이 대부분이었다. 이분 탐색을 사용하면 내가 해결한 정도의 시간이 나오는게 정상이니 안심한다.

Tags:

Categories:

Updated:

Leave a comment