백준 문제/BFS,DFS

12851: 숨바꼭질 2

goldpig 2024. 4. 5. 12:30
728x90

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

 

12851번: 숨바꼭질 2

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

www.acmicpc.net

설명

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때 걷는다면 1초 후에 X-1 또는 X+1로 이동하게 된다. 순간이동을 하는 경우에는 1초 후에 2*X의 위치로 이동하게 된다.

수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 그리고, 가장 빠른 시간으로 찾는 방법이 몇 가지 인지 구하는 프로그램을 작성하시오.

핵심 아이디어

  • 두 개의 위가 주어졌을 때, 한 위치에서 다른 위치로 이동하는 가장 빠른 시간과 방법을 구하는 문제이므로 BFS 탐색 알고리즘을 떠올리면 좋다. 
  • x-1, x+1, x*2 를 고려해서 범위 내에 있고, 방문하지 않았거나 방문 했더라도 이전 지점에서 재방문 할 수 있을 경우의 조건을 넣어야 한다. 
  • 그리고 K값이 되면, 방문 거리와 방문 횟수를 저장해 주었다. 그리고 연산이 처음부터 돌아가야 하므로 continue를 넣어 주었다. (continue를 넣지 않으면 중간 테스트케이스에서 틀려버린다.)

코드 구현

Python

import sys
input = sys.stdin.readline
from collections import deque

start, k = map(int, input().split())
distance = [0] * 100001 # 최대 0부터 100000지점까지 걸리는 횟수 기록
#bfs 탐색 알고리즘
q = deque()
q.append(start) # 수빈이가 있는 시작 위치

min_distance = 0 # 최적 시간
cnt = 0 # 최적의 경로의 수

while q:
	x = q.popleft()
	# 도작 지점이라면 탐색 종료
	if x == k: # 방문 시간과 방문 횟수 저장
		min_distance = distance[x]
		cnt += 1
		continue # 다른 최단 방법을 찾기 위해 연산을 처음부터 하기 위함
	for next in (x+1, x-1, x*2):
		# 주어진 범위를 벗어나지 않고, 방문하지 않았거나 다음 지점 의 최단 거리가
		# 이전 지점 + 1 이라면 즉, 동일한 최단 거리 기록을 갖는 곳
		# 중요한 점은 동일한 탐색 횟수를 가진 곳도 재탐색 해야한다.
		# 이를 통해 이미 탐색 했던 곳도 포함해서 다양한 최단 경로 탐색 가능
		if (0<= next <= 100000) and (distance[next] == 0 or
		distance[next] == distance[x] + 1):
			distance[next] = distance[x] + 1 # 방문하고 횟수 1 늘림
			q.append(next)
print(min_distance)
print(cnt)

C++

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;

int start, k;
vector<int> distance; // 최대 0부터 100000까지의 횟수 기록


int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin >> start >> k;
	// distance 초기화
	vector<int> distance(100001, 0);

	// 큐 구현을 위해 deque 사용
	queue<int> q;
	q.push(start); // 수빈이가 있는 시작 위치

	int min_distance = 0; // 최적 시간
	int cnt = 0; // 최적의 경로의 수

	while (!q.empty()) {
		int x = q.front();
		q.pop();

		// 도착 지점이라면
		if (x == k) {
			min_distance = distance[x]; // 첫 도착 시간 기록
			cnt++; // 같은 최단 거리일 경우 카운트 증가
			continue;
		}

		for (int next : {x + 1, x - 1, x * 2}) {
			// 주어진 범위를 벗어나지 않고, 방문하지 않았거나 
			// 다음 지점의 최단 거리가 이전 지점 + 1 이라면
			if (0 <= next && next <= 100000 &&
				(distance[next] == 0 || distance[next] == distance[x] + 1)) {
				distance[next] = distance[x] + 1; // 방문 하고 횟수 1 늘림
				q.push(next);
			}
		}
	}
	cout << min_distance << "\n";
	cout << cnt << "\n";

	return 0;
}

여기서 중요한 점은 '동일한 탐색 횟수를 가진 곳'도 탐색한다는 것이다. 즉 이미 탐색 했던 곳도 포함해서 다양한 최단 경로를 탐색할 수 있다. 그렇지 않으면, 앞선 최단 경로가 지나간 경로는 사용할 수 없으므로 모든 경우의 수를 구할 수 없다. 

728x90