C++/알고리즘

벽장문의 이동(DP)

K.DJ 2023. 5. 10. 14:54

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

 

2666번: 벽장문의 이동

첫 번째 줄에 벽장의 개수를 나타내는 3보다 크고 20보다 작거나 같은 하나의 정수, 두 번째 줄에 초기에 열려있는 두 개의 벽장을 나타내는 두 개의 정수, 그리고 세 번째 줄에는 사용할 벽장들

www.acmicpc.net

 

기본적으론 완전 탐색을 기반으로 두고

이미 탐색했던 상황에 대해선 저장된 값을 사용하는 DP 기법을 활용하는 문제다.

 

처음엔 특정 문을 열때 각각 왼쪽, 오른쪽에 있는 벽장으로부터 열 수 있다는 점에서 착안하여 DP[21][2]의 이중 어레이를 사용하려고 했다.

하지만 특정 벽장을 열때마다, 즉 단계마다 열려있는 벽장의 정보를 저장할 수가 없었다.

 

해당 문제에서 중요한 점은 각 상황에 따라 열려있는 벽장이 달라진다는 점이다.

모든 상황을 염두에 두는 완전 탐색을 진행할 것이므로 DP의 인덱스로 '상황(단계)'과 '열려있는 두 문'을 사용한다.

 

DP[k][i][j] = k번째 단계에서 i, j 방이 열려있을 때 벽장문을 전부 사용하는데 필요한 최소 이동값

 

이 상황에서 앞서 말한 왼쪽, 오른쪽에 있는 벽장으로부터 여는 상황을 비교하여 더 작은 값을 찾도록 한다.

a번째 벽장문이 열려있을 때 b의 벽장문을 열고싶다면 abs(a - b)의 이동값을 갖게 된다.

 

#include <iostream>
#include <cstring>
using namespace std;

int DP[21][21][21];
// DP[k][i][j] = k번째 단계에서 i, j방이 열려있을때
// 벽장문을 전부 사용하는데 필요한 최소 이동값
int N, len;
int dest[21];

int DFS(int step, int x, int y)
{
	if (step >= len + 1) return 0; // step을 넘어가면 0을 반환
	if (DP[step][x][y] != -1) return DP[step][x][y]; // 이미 저장된 값이 있으면 사용

	int next = dest[step];
	DP[step][x][y] = min(
		abs(x - next) + DFS(step + 1, next, y), 
		abs(y - next) + DFS(step + 1, x, next)
	);
	return DP[step][x][y];
}

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

	int op1, op2;
	cin >> N >> op1 >> op2 >> len;

	for (int i = 1; i <= len; i++)
	{
		cin >> dest[i];
	}

	memset(DP, -1, sizeof(DP));
	cout << DFS(1, op1, op2);
}