본문 바로가기

백준 PS일지/DFS&BFS

[백준/Swift] 1697 : 숨바꼭질

 

 

 

https://www.acmicpc.net/problem/1697

 

1697번: 숨바꼭질

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일

www.acmicpc.net

 

문제 소개

 

한 수평선의 어딘가에 수빈이랑 동생이 있습니다. 

수빈이는 걷거나, 순간이동을 해서 동생을 찾아야 하는데 이때 가장 빠른시간을 구하는 문제입니다.

(맨 처음에 서로 다른 x,y축인줄..)

 

이 문제도 BFS로 풀 수 있습니다. 제가 기존에 풀었던 문제 대부분은 탐색을 하기 위해 "상, 하, 좌, 우"가 문제에서 주어졌다면.. 이문제는 bfs 탐색을 하기 위해서  특정 위치 -1 , 특정 위치 +1, 특정 위치 * 2 를 통해서 수빈이가 있는 좌표까지 탐색하 가면 됩니다!!

 

   문제에서 주어진 키워드

  • 수빈이의 점 = N (0과 100,000 사이)
  • 동생 점        = K (0과 100,000 사이)
  • 수빈이 걸을 때 = (특정 위치)+ 1 , (특정 위치)-1
  • 수빈이 순간이동 = (특정 위치)*2
  • 걷거나 순간이동은 1초마다 할 수 있다!

 

풀이 과정

 

bfs탐색을 하기 위해서 문제에서 주어진 1초 후에~ 라는 문장을 바탕으로 1초마다 bfs 탐색을 수행했습니다.

이 문제 풀기 전에 탈출 문제 풀었는데 상당히 비슷하네요

 

bfs 탐색을 하기 전에

var queue   = [(Int,Int)]()

튜플 형식의 list를 큐로 선언했습니다.

왼쪽 값은 수빈이가 걷거나, 순간이동해서 도착한 좌표입니다.

오른쪽 값은 방문 당시의 시간입니다.

 

수빈이가 bfs탐색을 하면서 방문한 시간 현재 시간과 같으면 현재 시간을 +1로 증가하면서 탐색을 이어나갑니다.

 

제가 푼 코드에 의하면. 초기

 

현재 시간(time)은 0 입니다.

초기에 수빈이가 5 좌표였으므로 초기값을 저장할 때는

queue.append((5,0))이 됩니다.

 

이때 현재시간과 queue에 들어있는 시간이 같아질 경우에 현재시간을 +1 합니다.

 

 

그리고 bfs탐색을 수행 후 새로 방문한 좌표들과 그때 시간은 (1초 뒤에 걸을 수 있다! == 1초 뒤에 새로운 좌표로 이동할 수 있다)

이전에 튜플의 오른쪽에 저장된 0 (초) 에서 +1 을 증가시켜 1을 저장합니다.

 

이후 while문을 수행하면서 queue에 들어있는 좌표를 전부 탐색할 때까지 bfs 탐색을 수행합니다.

 

특정 위치에서 탐색을 할 때 저는 클래스를 통해 구현을 했습니다.

 

/**
 * 이 클래스를 통해서 특정 location에 대한 텔레포트, 한칸 앞, 한칸 뒤 연산이 실행됩니다.
 * @param location : 현재 위치
 * @param teleport : 현재 위치 *=2
 * @param front      : 현재 위치 += 1
 * @param back      : 현재 위치 -= 1
 */
class move
{
    var location : Int
    var teleport : Int
    var front    : Int
    var back     : Int
    init(_ location : Int)
    {
        self.location = location
        teleport      = location * 2
        front         = location + 1
        back          = location - 1
    }
}

 

코드 구현

 

 

import Foundation

/**
 * 이 클래스를 통해서 특정 location에 대한 텔레포트, 한칸 앞, 한칸 뒤 연산이 실행됩니다.
 * @param location : 현재 위치
 * @param teleport : 현재 위치 *=2
 * @param front      : 현재 위치 += 1
 * @param back      : 현재 위치 -= 1
 */
class move
{
    var location : Int
    var teleport : Int
    var front    : Int
    var back     : Int
    init(_ location : Int)
    {
        self.location = location
        teleport      = location * 2
        front         = location + 1
        back          = location - 1
    }
}
/**
 * 최초 수빈과 동생의 위치를 입력받는 함수입니다.
 */
class locationDTO
{
    var subin  : Int
    var sister : Int
    init()
    {
        if let input = readLine()
        {
            let subin_sister = input.split(separator: " ").map{Int($0)!}
            subin  = subin_sister[0]
            sister = subin_sister[1]
        }else
        {
            subin  = -1
            sister = -1
        }
    }
}
/**
 * 동생을 찾기위한 탐색(bfs)!! 이 일어나는 함수입니다.
 * @param queue   :  (특정 위치, 시간)
 * @param index    : 큐를 탐색하기 위한 index
 * @param visited   : 특정 위치 방문 기록
 * @param prevSec : 이전 시간 체크!
 *
 * queue에 저장된 시간에 따라서 현재시간이 아니라면 계속적으로 큐에서 원소를 찾아 수빈이가 이동합니다.
 * -> bfs를 구현하기 위해 시간 개념 도입!!
 */
func findSister(_ location : locationDTO, _ second : inout Int)
{
    
    var queue   = [(Int,Int)]()
    var index   = 0
    var visited = Array(repeating: false, count: 100001)
    var prevSec = 0
    visited[0]  = true
    queue.append((location.subin,second))
    visited[location.subin] = true
    
    while queue.count != index
    {
        let locate = queue[index].0
        let nSec   = queue[index].1
        index += 1
        var moved  = move(locate)
        if nSec == second { second += 1 }
        if moved.teleport == location.sister ||
            moved.back == location.sister ||
            moved.front == location.sister
        {
            return
        }
        if moved.teleport > 0 && moved.teleport < 100001
        {
            if !visited[moved.teleport]
            {
                queue.append((moved.teleport,nSec + 1))
                visited[moved.teleport] = true
            }
        }
        if moved.front > 0 && moved.front < 100001
        {
            if !visited[moved.front]
            {
                queue.append((moved.front, nSec + 1))
                visited[moved.front] = true
            }
        }
        if moved.back > 0 && moved.back < 100001
        {
            if !visited[moved.back]
            {
                queue.append((moved.back,nSec + 1))
                visited[moved.back] = true
            }
        }
    }
}
func BOJ_1609()
{
    let location = locationDTO()
    var second   = 0
    if location.sister == location.subin
    {
        print(0)
        return
    }
    findSister(location, &second)
    print(second)
}
BOJ_1609()

 

 

개선해야 할 점

 

 * 그리고 moved의 멤버변수들은 범위가 1~ 100,000 범위여야 합니다.
 * ㅠㅠ;; 범위 나오는거 항상 반례 생각안하고 무조건 제출했다가 항상틀리네요.
 * 꼼꼼해져야겠어요+_+

 

  • 첫 제출 때 틀린 이유 : visit를 통해 1~100,000까지 방문체크를 하는데 마이너스가 나온다면? 을 틀리고서야 생각했다..
  • 두번째 제출 때 틀린 이유 : 이번엔 반대로 큰 숫자가 나오는 예외처리를 하지 않았다
  • 음..
  • 문제에서 범위가 주어진다면 그에대한 예외처리와 반례들을 구하는 방법을 연습해야 겠다.
  • 그리고 좀 더 꼼곰히...

'백준 PS일지 > DFS&BFS' 카테고리의 다른 글

[백준/Swift] 2146 : 다리 만들기  (0) 2022.06.29
[백준/Swift] 5427 : 불  (0) 2022.06.29
[백준/Swift] 3055 : 탈출  (0) 2022.06.24
[백준/Swift] 14502 : 연구소  (0) 2022.06.23