Coding Test/BOJ

# 16197. 두 동전

soyw906 2023. 3. 5. 22:28
관련 알고리즘 : 그래프 탐색, 백트래킹

 

문제 링크 : https://www.acmicpc.net/problem/16197

 

16197번: 두 동전

N×M 크기의 보드와 4개의 버튼으로 이루어진 게임이 있다. 보드는 1×1크기의 정사각형 칸으로 나누어져 있고, 각각의 칸은 비어있거나, 벽이다. 두 개의 빈 칸에는 동전이 하나씩 놓여져 있고,

www.acmicpc.net


문제 풀이

 

문제의 조건을 보면,

  • NxM 크기의 보드
  • 동전 2개 ( 서로 다른 빈 칸에 존재)
  • 각 보드의 칸에 동전 / 벽 / 혹은 빈 칸 존재
  • 상, 하, 좌, 우 이동 가능한 4개의 버튼
  • 버튼으로 동전 2개 동시조작 ( 동전은 동시에 같이 이동 )
    • 이동하려는 칸이 벽일 시 => 동전 이동 X
    • 이동하려는 칸이 없을 시 => 동전 떨어짐

두 동전 중 하나만 보드에 떨어뜨려야 하며, 최소 버튼을 몇 번 눌러야 하는지가 문제에서 원하는 조건이다.

 

버튼을 눌러 동전을 굴렸을 때, 발생할 수 있는 상황은 다음 3가지이다.

  • 2개의 동전 중, 2개 모두 보드 밖으로 떨어지는 경우
  • 2개의 동전 중, 2개 모두 보드 내에 존재하는 경우 
  • 2개의 동전 중, 1개만 보드 밖으로 떨어지는 경우 ( 문제에서 원하는 경우 )

해당되는 모든 경우의 수를 탐색하여 문제를 해결할 수 있었다.

 


C++ 코드

 

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

using namespace std;

int n, m;
int ans; // 답
char board[20][20]; // 보드

vector<pair<int, int>> coin; // 동전 위치 저장용 vector

int dx[] = { 0, 0, 1, -1 }; // 좌우 이동
int dy[] = { 1, -1, 0, 0 }; // 상하 이동

bool out(int a, int b) // 동전이 보드 밖으로 이탈 시 false 반환
{
    if (a < 0 || b < 0 || a >= n || b >= m) return true;
    return false;
}

void move(int coin1_x, int coin1_y, int coin2_x, int coin2_y, int cnt, int dir) // 동전 움직이기: move(동전1x, 동전1y, 동전2x, 동전2y, 버튼 누른 횟수, 방향)
{
    if (ans < cnt) 
        return; // 다른 경우의 결과보다 더 많이 버튼을 눌러야 한다면 => 최솟값이 이미 아니므로 탈출

    if (cnt > 10) // 이동 횟수 10번 이상일 경우                      
    {
        ans = min(ans, cnt); // ans값 갱신 후 탈출
        return;
    }

    int nx1 = coin1_x + dx[dir];
    int ny1 = coin1_y + dy[dir];
    int nx2 = coin2_x + dx[dir];
    int ny2 = coin2_y + dy[dir];

    if (out(nx1, ny1) == true && out(nx2, ny2) == true) // 2개 모두 떨어질 시
        return; 
    else if (out(nx1, ny1) == true && out(nx2, ny2) == false) // 2개 중 1개만 떨어질 시
    {
        ans = min(ans, cnt); // 최솟값 갱신
        return; // 결과 도출
    }
    else if (out(nx1, ny1) == false && out(nx2, ny2) == true)
    {
        ans = min(ans, cnt);
        return;
    }

    if (board[nx1][ny1] == '#') // 이동한 곳이 벽에 막힌 곳일 시, 이동 불가
    {
        nx1 = coin1_x;
        ny1 = coin1_y;
    }
    if (board[nx2][ny2] == '#')
    {
        nx2 = coin2_x;
        ny2 = coin2_y;
    }

    for (int i = 0; i < 4; i++) // 위 조건 모두 통과 시, 진행 
    {
        move(nx1, ny1, nx2, ny2, cnt + 1, i);
    }
}

void solve()
{
    ans = 99999999; // 최솟값과 비교해야 하므로 큰 수로 설정

    cin >> n >> m;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cin >> board[i][j];
            if (board[i][j] == 'o') coin.push_back(make_pair(i, j)); // 동전 위치 저장
        }
    }
    
    for (int i = 0; i < 4; i++)
    {
        int x1 = coin[0].first;
        int y1 = coin[0].second;
        int x2 = coin[1].first;
        int y2 = coin[1].second;

        move(x1, y1, x2, y2, 1, i);
    }

    if (ans > 10)
        ans = -1;

    cout << ans << "\n";
}

int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    solve();

    return 0;
}