


수레를 옮길 때 두 수레를 1 카운트 하면서 dfs를 통해 옮기는 방식을 선택하였다.
왜 이 방식을 선택했냐면 "동시에 두 수레를 같은 칸으로 움직일 수 없습니다." 이 조건에 대해 어떤 수레를 먼저 움직이는지에 따라 결과가 달라지기 때문에 완전탐색을 위해 dfs를 선택하였다.
그리고 종료 조건을 둘 다 목적지에 도착했을 때 종료시켜주면서 모든 경우의 수 중 최소값을 탐색한다 (칸이 4*4라서 충분히 가능할 듯)
import java.io.*;
import java.util.*;
class Solution {
static int n,m,min;
static int[][] visitedA, visitedB;
static int[] sA,eA,sB,eB;
static int[] dr = {-1,0,1,0};
static int[] dc = {0,1,0,-1};
public int solution(int[][] maze) {
int answer = 0;
n = maze.length;
m = maze[0].length;
visitedA = new int[n][m];
visitedB = new int[n][m];
sA = new int[2];
eA = new int[2];
sB = new int[2];
eB = new int[2];
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(maze[i][j] == 1){
sA[0] =i; sA[1] = j;
visitedA[i][j] = 1;
}
if(maze[i][j] == 2){
sB[0] =i; sB[1] = j;
visitedB[i][j] = 1;
}
if(maze[i][j] == 3){
eA[0] =i; eA[1] = j;
}
if(maze[i][j] == 4){
eB[0] =i; eB[1] = j;
}
}
}
min = Integer.MAX_VALUE;
dfs(sA,sB,maze,0,visitedA,visitedB);
if(min == Integer.MAX_VALUE) answer = 0;
else answer = min;
return answer;
}
public void dfs(int[] startA, int[] startB, int[][] map, int count, int[][] vA, int[][] vB){
// System.out.println(startA[0] + " " + startA[1] + " || " + startB[0] + " " + startB[1] + " || count = " + count);
if(startA[0] == eA[0] && startA[1] == eA[1] && startB[0] == eB[0] && startB[1] == eB[1]){
if(min > count) min = count;
return;
}
// 빨간 수레 가능한 이동 위치
ArrayList<int[]> aList = new ArrayList<>();
// 빨간 수레가 도착하지 않은 경우에만 이동 가능
if(!(startA[0] == eA[0] && startA[1] == eA[1])) {
for(int i=0;i<4;i++){
int[] addA = new int[2];
int nr = startA[0] + dr[i];
int nc = startA[1] + dc[i];
if(nr < 0 || nc < 0 || nr >= n || nc >= m) continue;
if(vA[nr][nc] == 1) continue;
if(map[nr][nc] == 5) continue;
addA[0] = nr;
addA[1] = nc;
aList.add(addA);
}
} else {
// 빨간 수레가 도착한 경우 현재 위치 유지
aList.add(new int[]{startA[0], startA[1]});
}
// 파란 수레 가능한 이동 위치
ArrayList<int[]> bList = new ArrayList<>();
// 파란 수레가 도착하지 않은 경우에만 이동 가능
if(!(startB[0] == eB[0] && startB[1] == eB[1])) {
for(int i=0;i<4;i++){
int[] addB = new int[2];
int nr = startB[0] + dr[i];
int nc = startB[1] + dc[i];
if(nr < 0 || nc < 0 || nr >= n || nc >= m) continue;
if(vB[nr][nc] == 1) continue;
if(map[nr][nc] == 5) continue;
addB[0] = nr;
addB[1] = nc;
bList.add(addB);
}
} else {
// 파란 수레가 도착한 경우 현재 위치 유지
bList.add(new int[]{startB[0], startB[1]});
}
// 모든 이동 조합 시도
for(int[] tmpA : aList){
for(int[] tmpB : bList){
// 같은 칸으로 이동 방지
if(tmpA[0] == tmpB[0] && tmpA[1] == tmpB[1]) continue;
// 자리 바꾸기 방지
if(startA[0] == tmpB[0] && startA[1] == tmpB[1] &&
startB[0] == tmpA[0] && startB[1] == tmpA[1]) continue;
// 방문 배열 복사 및 업데이트
int[][] newVA = new int[n][m];
int[][] newVB = new int[n][m];
for (int i = 0; i < n; i++) {
newVA[i] = Arrays.copyOf(vA[i], m);
newVB[i] = Arrays.copyOf(vB[i], m);
}
// 실제로 이동한 경우에만 방문 표시
if(!(startA[0] == eA[0] && startA[1] == eA[1])) {
newVA[tmpA[0]][tmpA[1]] = 1;
}
if(!(startB[0] == eB[0] && startB[1] == eB[1])) {
newVB[tmpB[0]][tmpB[1]] = 1;
}
dfs(tmpA, tmpB, map, count+1, newVA, newVB);
}
}
}
}

빨간 수레가 가능한 이동 위치를 aList에 담고, 파란 수레가 가능한 이동 위치를 bList에 담는다. 그 후에 모든 이동 조합을 한 후 같은 칸으로 이동을 막아주고, 자리 바꾸기를 통한 이동을 막아준다 (이거를 처음에 안해서 계속 81점 나옴..) 그렇게 되면 가능한 둘 수 있는 방법을 모두 두고 count+1 하면서 모든 경로를 파악할 수 있다.
int[][] newVA = new int[n][m];
int[][] newVB = new int[n][m];
for (int i = 0; i < n; i++) {
newVA[i] = Arrays.copyOf(vA[i], m);
newVB[i] = Arrays.copyOf(vB[i], m);
}
dfs(tmpA, tmpB, map, count+1, newVA, newVB);
수레는 각각의 움직인 경로를 다시 밟을 수 없다 그래서 모든 경로를 파악하면서 백트래킹을 통해 visited 배열을 관리해줘야 한다. 그래서 newVA, newVB를 생성해서 그 당시의 visited배열을 이렇게 관리해주어야 백트래킹이 성공한다.
프로그래머스라 분류를 모르겠는데 백트래킹, 구현문제인 것 같다. 사실 맵 사이즈가 정말 너무 작아서 완전탐색을 통해서 푼 것인데 최적화 할 수 있는 방법이 있을까 궁금한 문제인 것 같다.