호우동의 개발일지

Today :


문제 이해 단계

 

문제 자체는 짧고 이해도 간단한 문제.

X축 하나만 존재하고 점 N에서 점 K까지 가는데 총 3가지 방법이 존재한다.

1. 2 * N칸 이동 -> 시간소모 = 0초
2. +1칸 이동 -> 시간소모 = 1초
3. -1칸 이동 -> 시간 소모 = 1초


이 세 가지 방법을 이용하여, 점 N에서 점 K까지 가는 최소 시간을 찾는 문제이다.

 

 


문제 접근 단계

처음에는 최소시간을 찾는 것이기 때문에 그리디 문제인 줄 알았다.

하지만 그리디 문제의 조건은 매 순간마다의 최적의 답이 문제의 답이 되어야 한다.


이 문제에서는 나중에 분석한 답이 더 최소 시간 일수도 있기 때문에
그리디로 풀기엔 적합하지 않다.

그래서 x축에서 한 점을 기준으로 양쪽을 탐색하여 K를 찾는 BFS 방식을 사용하기로 했다.

다른 BFS 문제와 다르게 이동하는 데에 가중치가 존재한다.

2x로 이동하는 데는 0의 비용이 존재하고,
(x+1)과 (x-1)에는 1의 비용이 존재한다.


즉, 2x가 (x+1)과 (x-1)보다 우선순위가 높다.


이게 뜻하는 말은 한 칸씩 이동하는 탐색보다
두 배씩 이동하는 탐색이 먼저 수행되어야 한다는 뜻이다.

이를 구현하기 위해서 덱 또는 우선순위 큐를 사용해야 하는데, 이번에는 덱을 사용하였다.

2x 작업을 할 때는 덱 앞에서 넣어주고, x+1이나 x-1을 넣어줄 때는 덱 뒤에서 넣어준다.


덱에서 뽑는 작업을 할 때는 덱 앞에서부터 뽑아, 무조건 2x작업이 먼저 수행되도록 해준다.

 

 


문제 구현 단계

	int N, K;
	int dx[] = { -1,1 };
	int visit[100001];
	int answer = 0;
	cin >> N >> K;
	deque<int> dq;
	dq.push_back(N);
	visit[N] = 1;

+- 작업을 해줄 배열 dx []와 방문 처리를 담당할 visit,
그리고 덱을 선언한 뒤, 입력 N을 큐에 넣고 visit을 1로 초기화해 준다.


여기서 visit은 방문처리를 뜻하기도 하지만, 방문 최소 시간을 담는 배열이기도 하다.

	while (!dq.empty()) {
		int x = dq.front();
		if (x == K) {
			answer = visit[x] - 1;
			break;
		}
		dq.pop_front();
		if (2 * x < 100001 && !visit[2 * x]) {
			visit[2 * x] = visit[x];
			dq.push_front(2 * x);
		}
		for (int i = 0; i < 2; i++) {
			int nx = x + dx[i];
			if (nx > 100000 || nx < 0 || visit[nx]) continue;
			dq.push_back(nx);
			visit[nx] = visit[x] + 1;
		}
	}
	cout << answer;

BFS를 하는 부분이다.

기본적인 BFS방식과 동일하나, 덱에서 빼낸 값이 K와 동일하면 그 값에서 -1을 해준다.
왜냐하면 시간은 0초부터 시작해야 하는데 방문처리 때문에 1로 초기화했기 때문이다.

이후에는 2x가 최대범위 안에 있고 방문하지 않았으면, x에 있던 시간 정보 그대로를 2x에 전달하고 덱 앞에 넣는다.

x+1, x-1인 경우에는 2x와 똑같은 작업을 하는데 시간정보 + 1을 전달하고 덱 뒤에 넣는다.

이 작업을 덱이 빌 때까지 혹은, x가 K값과 같아질 때까지 반복한다.

밑에는 전체코드를 올려두고 끝내겠다.

#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;

int main() {

	int N, K;
	int dx[] = { -1,1 };
	int visit[100001];
	int answer = 0;
	cin >> N >> K;
	deque<int> dq;
	dq.push_back(N);
	visit[N] = 1;
	while (!dq.empty()) {
		int x = dq.front();
		if (x == K) {
			answer = visit[x] - 1;
			break;
		}
		dq.pop_front();
		if (2 * x < 100001 && !visit[2 * x]) {
			visit[2 * x] = visit[x];
			dq.push_front(2 * x);
		}
		for (int i = 0; i < 2; i++) {
			int nx = x + dx[i];
			if (nx > 100000 || nx < 0 || visit[nx]) continue;
			dq.push_back(nx);
			visit[nx] = visit[x] + 1;
		}
	}
	cout << answer;
}

 

 


시행착오

그래프 탐색 문제 중에서 우선순위가 존재하는, 가중치가 존재하는 문제는 처음이었다.

엄청 시간이 오래 걸려서 풀었으나 메모리초과, 틀렸습니다 등,
내 선에서는 도저히 풀 수가 없었다.


결국에는 인터넷을 참고할 수밖에 없었다.
신선한 문제였기도 하고 이제 이런 문제도 풀어봤으니
앞으로는 좀 더 쉽게 풀 수 있을 것 같다.