코딩테스트 문제 풀이 (C++)

백준 12866 - 돌 그룹 (C++)

오의현 2024. 6. 11. 15:03

 

 

3개의 돌 그룹이 있다. 각 그룹에는 돌이 A개, B개, C개씩 포함되어있다.

이 때, 두 개의 돌 그룹을 선택하여 돌의 개수를 바꾸는 연산을 진행한다.

개수를 바꾸는 진행은 두 그룹의 돌 개수가 X, Y 이고, X < Y라고 했을 때, X = 2 * X , Y = Y - X 의 형식으로 진행한다.

이 연산을 반복하여 A,B,C의 개수를 모두 동일하게 만들 수 있다면 1을 출력하고, 아니라면 0을 출력하면 된다.

 

문제 풀이

이 문제는 모든 경우를 탐색하는 방법 말고는 더 좋은 방법은 딱히 없는 듯 하다.

방문 체크를 하며, 이미 탐색한 경우는 제외하고 모든 경우에 대해 탐색하도록 하였다.

 

최초에 A, B, C가 주어진다.

 

연산은 두 그룹을 선택했을 때, 더 작은 쪽의 값을 작은 쪽에 더하고, 큰 쪽에서 빼는 연산을 하게 된다.

같은 양을 양쪽에 빼고 더하기 때문에, 전체 총량 (A + B + C)는 변하지 않는다.

 

즉, A와 B만 알면 사실 C는 구할 수 있다.

이 아이디어를 기준으로, 방문 체크는 3차원 배열이 아닌 2차원 배열을 사용하여 2개의 돌을 기준으로만 하게 된다.

 

또한, 1, 2, 3 과 3, 2, 1은 사실 같은 경우라고 할 수 있다.

문제에서 구하는 것은 연산을 반복했을 때, 모든 돌 그룹의 돌 개수가 같아질 수 있는지 여부를 반환하는 것이기 때문에 1,2,3 에서 true가 나온다면 3, 2,1에서도 당연히 true가 나올 수 밖에 없다.

 

그렇기 때문에, 돌 개수를 정렬하여 탐색을 할 것이다.

(1, 2, 3 과 3, 2, 1을 같은 경우라고 가정하기 위해)

 

처음 A, B, C가 있다면, 이 중 2개의 돌 A, B만 선택하여 Queue에 삽입한다.

이후, Queue에서는 front를 pop한 뒤, A,B,C를 기준으로 BFS를 진행할 것이다.

 

1. A, B를 선택하는 경우

2. A, C를 선택하는 경우

3. B, C를 선택하는 경우

 

세 경우를 탐색하며 계속 BFS를 진행하며, A,B,C가 같아지는 타이밍이 온다면 1을 출력하고 종료하도록 하였다.

만약, BFS가 모두 끝날 때 까지 A,B,C가 같아지는 타이밍을 발견하지 못한다면 0을 출력하도록 하였다.

 

또한, A, B, C 가 절대 같아질 수 없는 경우가 한 가지 있는데, A + B + C의 값이 3의 배수가 아닌 경우이다.

A, B, C가 모두 X라는 값으로 같은 값을 가진다면 A + B + C 는 3X 가 된다.

즉, A, B, C 가 같다면, A + B + C 는 반드시 3의 배수여야 한다.

 

그러므로, A + B + C가 3의 배수가 아니라면 바로 0을 출력하고 종료하도록 예외처리를 해주었다.

 

풀이 코드

int A = 0;
int B = 0;
int C = 0;

std::cin >> A >> B >> C;

int SumStone = A + B + C;

if (SumStone % 3 != 0)
{
    std::cout << 0;
    return 0;
}

먼저, 입력을 받아준 뒤 세 수의 합이 3의 배수가 아니라면 0을 출력한 뒤 종료하도록 하였다.

std::queue<std::pair<int, int>> BFS;

if (A > B)
{
    std::swap(A, B);
}

BFS.push({A, B});

다음은 queue에 첫 원소인 {A, B}를 삽입해 주었다.

이 때, A와 B를 오름차순으로 정렬하여 넣어주어야 한다.

while (BFS.size() > 0)
{
    std::pair<int, int> CurStone = BFS.front();
    BFS.pop();

    int A = CurStone.first;
    int B = CurStone.second;
    int C = SumStone - A - B;

    if (A == B && B == C)
    {
        std::cout << 1;
        return 0;
    }

    if (A != B)
    {
        InsertQueue(A, B, BFS);
    }

    if (B != C)
    {
        InsertQueue(B, C, BFS);
    }

    if (A != C)
    {
        InsertQueue(A, C, BFS);
    }
}

BFS내부 코드이다.

 

먼저, C를 구해준다. 그리고, A와 B와 C가 모두 같은지 검사해준다. 만약 같다면 1을 출력하고 그대로 종료한다.

같지 않다면, 그대로 너비 우선 탐색을 진행한다.

 

두 그룹을 선택했을 때, 돌의 개수가 같다면 선택할 수 없으므로 두 그룹의 돌 개수가 다른 경우에만 탐색을 진행하도록 하였다.

void InsertQueue(int _Left, int _Right, std::queue<std::pair<int, int>>& _Queue)
{
    if (_Left > _Right)
    {
        std::swap(_Left, _Right);
    }

    _Right -= _Left;
    _Left *= 2;

    if (isVisit[_Left][_Right] == false)
    {
        isVisit[_Left][_Right] = true;
        _Queue.push({ _Left, _Right });
    }
}

이는 queue에 원소를 삽입하는 함수이다.

동일한 코드를 여러 번 작성하는 것이 불편하니, 함수로 만들어주었다.

 

먼저, 선택된 두 숫자를 오름차순으로 정렬하였다.

그리고, 연산을 진행하였다. 큰 수에서는 작은 수를 빼주고, 작은 수에는 작은 수를 더해주었다.

 

그렇게 나온 경우를 이미 탐색한 경험이 없다면, 방문 체크를 한 뒤 queue에 삽입해주었다.

std::cout << 0;

return 0;

만약, 반복문이 끝날 때 까지 프로그램이 종료되지 않았다면 세 그룹의 돌 개수가 같은 경우를 발견하지 못한 것이므로 0을 출력하고 끝내주었다.

 

 

코드 전문

더보기
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <set>

void Init()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
}

std::vector<std::vector<bool>> isVisit(1501, std::vector<bool>(1501, false));

void InsertQueue(int _Left, int _Right, std::queue<std::pair<int, int>>& _Queue)
{
    if (_Left > _Right)
    {
        std::swap(_Left, _Right);
    }

    _Right -= _Left;
    _Left *= 2;

    if (isVisit[_Left][_Right] == false)
    {
        isVisit[_Left][_Right] = true;
        _Queue.push({ _Left, _Right });
    }
}

int main()
{
    Init();

    int A = 0;
    int B = 0;
    int C = 0;

    std::cin >> A >> B >> C;

    int SumStone = A + B + C;

    if (SumStone % 3 != 0)
    {
        std::cout << 0;
        return 0;
    }

    std::queue<std::pair<int, int>> BFS;

    if (A > B)
    {
        std::swap(A, B);
    }

    BFS.push({A, B});

    while (BFS.size() > 0)
    {
        std::pair<int, int> CurStone = BFS.front();
        BFS.pop();

        int A = CurStone.first;
        int B = CurStone.second;
        int C = SumStone - A - B;

        if (A == B && B == C)
        {
            std::cout << 1;
            return 0;
        }

        if (A != B)
        {
            InsertQueue(A, B, BFS);
        }

        if (B != C)
        {
            InsertQueue(B, C, BFS);
        }

        if (A != C)
        {
            InsertQueue(A, C, BFS);
        }
    }

    std::cout << 0;

    return 0;
}