-
백준 2666번 : 벽장문의 이동알고리즘 공부 2020. 5. 7. 14:31
처음 이 문제를 보고 단순히 가까운 것을 swap 하기만 하면 되는 줄 알고 'dp 쓸 필요도 없겠네~!'라고 좋아하면서 덤볐다가 코드를 짜면서 좀 더 생각해보니까 가까운 것만 swap하는 것이 최소가 아니라는 것을 깨닫고.... 이진트리를 이룬다는 사실을 깨닫고... ㅠㅠ 나의 멍청스러움에 슬퍼하면서 얌전히 dp를 사용했다.
풀이
dp[index][open1][open2]
open1과 open2번째의 벽장이 열려있을 때 index 단계의 벽장을 열기 위한 최소 이동횟수
'index 단계의 벽장을 연다'라는 말이 모호해서 추가설명을 하자면 문제의 예시에 3, 1, 6, 5번 벽장을 순서대로 이용하려고 한다고 되어있다. 이때 index=2라는 것은 '두번째 단계의 벽장을 이용한다', 즉 1번 벽장을 이용하려고 한다는 뜻이다. 또 index=3이라는 것은 '세 번재 단계의 벽장을 이용한다', 즉 6번 벽장을 이용하려고 한다는 뜻이다.
dp의 계산
우선 2번과 5번 벽장이 열려있는 상태에서 3번 벽장을 사용하려고 한다면 몇 번 이동해야 할까? 답은 간단하다. 닫혀있는 3번 벽장의 문을 열려있는 2번 벽장으로 이동 시키면 됨으로 1번이다. 만약 5번 벽장을 이용하려면 2번 이동시켜야하기 때문에 최소횟수가 아니다. 이를 식으로 표현하면 다음과 같다.
dp[index][open1][open2] = min(abs(open1 - target), abs(open2 - target))
target(= order[index])은 이용하려고 하는 벽장의 번호다. 하지만 이것만 고려하면 되는 것이 아니라 그렇게 벽장을 이동시킨 후의 상황도 고려해야한다. 예를들어보자. 1번부터 9번까지의 벽장이 있고 최초에는 1번과 9번이 열려있다고 해보자. 이때 5번 벽장을 이용 후 1번 벽장을 이용하려 한다고 하자. 이 경우 최소 횟수는 9번을 닫으며 5번을 열고 이미 열려있는 1번을 이용하는 것이다. 만약 1번을 닫으며 5번을 열면 다시 1번을 열기 위해 5번을 닫아야 해서 많은 이동이 필요하다. 따라서 그 순간의 이동 횟수만 고려하면 되는 것이 아니라 그 이후의 상황도 고려해야 한다.
open1을 닫으며 target을 열 경우 : abs(open1 - target) + dp[index+1][target][open2]
open2를 닫으며 target을 열 경우 : abs(open2 - target) + dp[index+1][open1][target]
따라서 위의 두 경우 중 min을 취하면 된다.
#include <iostream> #include <stdlib.h> #include <cstring> using namespace std; int n, t; int order[21]; int dp[21][21][21]; int count(int index, int open1, int open2) { if(index > t) return 0; int result = dp[index][open1][open2]; if(result != -1) return result; int target = order[index]; dp[index][open1][open2] = min(abs(open1-target) + count(index+1, target, open2), abs(open2-target) + count(index+1, open1, target)); return dp[index][open1][open2]; } int main() { ios_base :: sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); memset(dp, -1, sizeof(dp)); cin >> n; int open1, open2; cin >> open1 >> open2; cin >> t; for(int i = 1; i <= t; i++) { cin >> order[i]; } cout << count(1, open1, open2); return 0; }
'알고리즘 공부' 카테고리의 다른 글
백준 2629번 : 양팔저울 (0) 2020.05.10 백준 2616번 : 소형기관차 (0) 2020.05.09 백준 2229번 : 조 짜기 (0) 2020.05.07 백준 2228번 : 구간 나누기 (1) 2020.05.05 백준 1720번 : 타일 코드 (1) 2020.05.04