Algorithm/Programmers

[Lv.3] 경주로 건설

시롱시롱 2023. 4. 12. 18:38
import java.util.Arrays;

class Solution {
    static int n;
    static int minCost;

    static int[][] gameBoard;
    static boolean[][] visit;
    static int[][][] costDp;
    public int solution(int[][] board) {
        n=board.length;
        minCost=Integer.MAX_VALUE;
        gameBoard=board.clone();
        visit=new boolean[gameBoard.length][gameBoard.length];
        costDp=new int[gameBoard.length][gameBoard.length][2];
        for (int[][] ints : costDp) {
            for (int[] anInt : ints) {
                anInt[0]=anInt[1]=Integer.MAX_VALUE;
            }
        }
        //way : 1= Up,Down -1: Left,Right
        costDp[0][0][0]=costDp[0][0][1]=0;
        dfs(0,0,0,0);
        return minCost;
    }
    private void dfs(int i,int j, int cost,int way){
        if (i == n - 1 && j == n - 1) {
            minCost=Math.min(minCost,cost);
            return;
        }
        if(cost>minCost)
            return;

        //
        if(costDp[i][j][0]<cost&&costDp[i][j][1]<cost)
            return;

        if(i>0&&!visit[i-1][j]&&gameBoard[i-1][j]==0){
            //Go Up
            if(way==1 || way==0){
                //코너 없이 직선
                cost+=100;
                visit[i][j]=true;
                costDp[i-1][j][0]=Math.min(costDp[i-1][j][0],cost);
                dfs(i-1,j,cost,1);
                visit[i][j]=false;
                cost-=100;
            }else{
                //코너 생성 필요 (직전 움직임이 좌,우 였다)
                cost+=600;
                visit[i][j]=true;
                costDp[i-1][j][1]=Math.min(costDp[i-1][j][1],cost);
                dfs(i-1,j,cost,1);
                visit[i][j]=false;
                cost-=600;
            }
        }
        if(i<n-1&&!visit[i+1][j]&&gameBoard[i+1][j]==0){
            //Go Down
            if(way==1 || way==0){
                //코너 없이 직선
                cost+=100;
                visit[i][j]=true;
                costDp[i+1][j][0]=Math.min(costDp[i+1][j][0],cost);
                dfs(i+1,j,cost,1);
                visit[i][j]=false;
                cost-=100;
            }else{
                //코너 생성 필요 (직전 움직임이 좌,우 였다)
                cost+=600;
                visit[i][j]=true;
                costDp[i+1][j][1]=Math.min(costDp[i+1][j][1],cost);
                dfs(i+1,j,cost,1);
                visit[i][j]=false;
                cost-=600;
            }
        }
        if(j>0&&!visit[i][j-1]&&gameBoard[i][j-1]==0){
            //Go Left
            if(way==-1 || way==0){
                cost+=100;
                visit[i][j]=true;
                costDp[i][j-1][0]=Math.min(costDp[i][j-1][0],cost);
                dfs(i,j-1,cost,-1);
                visit[i][j]=false;
                cost-=100;
            }else{
                cost+=600;
                visit[i][j]=true;
                costDp[i][j-1][1]=Math.min(costDp[i][j-1][1],cost);
                dfs(i,j-1,cost,-1);
                visit[i][j]=false;
                cost-=600;
            }
        }
        if(j<n-1&&!visit[i][j+1]&&gameBoard[i][j+1]==0){
            if(way==-1 || way==0){
                cost+=100;
                visit[i][j]=true;
                costDp[i][j+1][0]=Math.min(costDp[i][j+1][0],cost);
                dfs(i,j+1,cost,-1);
                visit[i][j]=false;
                cost-=100;
            }else{
                cost+=600;
                visit[i][j]=true;
                costDp[i][j+1][1]=Math.min(costDp[i][j+1][1],cost);
                dfs(i,j+1,cost,-1);
                visit[i][j]=false;
                cost-=600;
            }
        }

    }
}

처음엔 DFS로만 구현했는데, 제출해보니 시간초과가 났다.

그래서, 2차원 dp를 섞어서 해당 노드까지의 비용이 저장된 비용보다 큰 경우 탐색을 종료하는 로직을 추가했더니, 2개의 테스트 케이스에서 실패가 떴다.

코너를 통해 들어온 경우에 코너를 통하지 않은 경로보다 비용이 당장은 비싸지만 향후 경로에서 이득인 경우가 발생할 수 있기 때문에 3차원dp를 만들어 비용을 검사하는 로직을 추가했다.

결과는, 무난하게 통과되었다 !