알고리즘문제/swea

등산로 조성[모의 SW 역량테스트] (자바)

indeep 2023. 4. 7. 16:50

https://swexpertacademy.com/main/talk/solvingClub/problemView.do?contestProbId=AV5PoOKKAPIDFAUq&solveclubId=AYWesuzK3nUDFAQK&problemBoxTitle=6%EC%A3%BC%EC%B0%A8&problemBoxCnt=5&probBoxId=AYdFW2sakzMDFASR+ 

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

 

이 문제에서 가장 핵심인 곳은 긴 등산로를 만들기 위해 딱 한 곳을 정해서 최대 K깊이 만큼 지형을 깎을 수 있다는 점이다.

K=4가 주어지면 이걸 1,1,1,1 or 1,1,2 이렇게 나눠서 깎는 것이 아니고 한 지점을 정해서 1~4까지 깎을 수 있는 것이다.

 

 

전체 코드를 우선 확인하자.

import java.util.Scanner;
 
public class Solution {
    static int[][] map;
    static boolean[][] visited;
    static int K;
    static int N;
    static int[] dr = { -1, 1, 0, 0 };
    static int[] dc = { 0, 0, -1, 1 };
    static int max;
 
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        for (int tc = 1; tc <= T; tc++) {
            max = Integer.MIN_VALUE;
            N = sc.nextInt();
            K = sc.nextInt(); // 땅파기
            map = new int[N][N];
            visited = new boolean[N][N]; // 방문체크
            int maxheight = 0; // 최대 봉우리
            // 봉우리 입력, 최대 봉우리 찾기
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    map[i][j] = sc.nextInt();
                    if (maxheight < map[i][j])
                        maxheight = map[i][j];
                }
            }
 
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (map[i][j] == maxheight) { // 최대 봉우리면 시작
                        DFS(i, j, 1, false); // 우선 땅을 안팠다고 주고 dfs 시작
                    }
                }
            }
            System.out.printf("#%d %d\n",tc,max);
        }
    }
 
    public static void DFS(int x, int y, int cnt, boolean dig) {
        visited[x][y] = true;
        if(cnt>max) max = cnt;
         
        for (int i = 0; i < 4; i++) {
            int idx = x + dr[i];
            int idy = y + dc[i];
            if (idx >= 0 && idy >= 0 && idx < N && idy < N) {
                // 안파고 가도 되는 경우
                if (!visited[idx][idy] && map[x][y] > map[idx][idy]) {
                    DFS(idx, idy, cnt + 1, dig); // dig는 변경이 없으니 그대로 넘긴다.
                }
                // 다음 봉우리가 더 높거나 같다면
                else if (map[x][y] <= map[idx][idy]) {
                    // 방문 안 했고, 안 팠고, K만큼 팠을 때 작아지면
                    if(!visited[idx][idy] && !dig && map[x][y] > map[idx][idy]-K) {
                        int temp = map[idx][idy]; // 임시로 저장해놔야 돌아올 때 원상복구
                        for(int j=1; j<=K; j++) { // 1부터 K까지 볼 건데
                            if(map[idx][idy]-j < map[x][y]) { // j를 뺐을 때 map[x][y]가 크면
                                map[idx][idy]-=j; // 빼고
                                DFS(idx, idy, cnt + 1, true); // 팠다고 하고 DFS돈다.
                                map[idx][idy] = temp; // 다시 원상복구
                            }
                        }
                    }
                } 
            }
        }
        visited[x][y] = false;
    }
}

 

 

지금부터 코드를 하나 살펴보겠다.

 

static int[][] map; // 입력받을 맵          
static boolean[][] visited; // 방문체크    
static int K; // 최대 공사 횟수              
static int N; // 맵의 크기                 
// 상, 하, 좌, 우를 탐색하기 위한 배열              
static int[] dr = { -1, 1, 0, 0 };     
static int[] dc = { 0, 0, -1, 1 };     
// 최대값 찾기                              
static int max;

스태틱으로 선언해서 사용하도록 만들었다.

 

public static void main(String[] args) {             
	Scanner sc = new Scanner(System.in);             
	int T = sc.nextInt();                            
	for (int tc = 1; tc <= T; tc++) {                
		max = Integer.MIN_VALUE;                     
		N = sc.nextInt();                            
		K = sc.nextInt(); // 땅파기                     
		map = new int[N][N];                         
		visited = new boolean[N][N]; // 방문체크         
		int maxheight = 0; // 최대 봉우리                 
		// 봉우리 입력, 최대 봉우리 찾기                         
		for (int i = 0; i < N; i++) {                
			for (int j = 0; j < N; j++) {            
				map[i][j] = sc.nextInt();            
				if (maxheight < map[i][j])           
					maxheight = map[i][j];           
			}                                        
		}

최대 봉우리 변수를 생성하고 입력을 넣음과 동시에 최대 봉우리를 찾게 만들었다.

 

 

 

		for (int i = 0; i < N; i++) {                                 
			for (int j = 0; j < N; j++) {                             
				if (map[i][j] == maxheight) { // 최대 봉우리면 시작           
					DFS(i, j, 1, false); // 우선 땅을 안팠다고 주고 dfs 시작      
				}                                                     
			}                                                         
		}                                                             
		System.out.printf("#%d %d\n",tc,max);                         
	}                                                                 
}

전체를 탐색하면서 최대봉우리면 DFS를 돌게 만들었다.

DFS에는 i,j의 좌표, 땅 시작 번호, boolean으로 땅을 팠는지 안 팠는지 신호를 같이 주었다.

false면 땅을 안 판 상태고, true면 땅을 판 상태이다.

 

 

 

public static void DFS(int x, int y, int cnt, boolean dig) {                
	visited[x][y] = true;                                                   
	if(cnt>max) max = cnt;                                                  
	                                                                        
	for (int i = 0; i < 4; i++) {                                           
		int idx = x + dr[i];                                                
		int idy = y + dc[i];                                                
		if (idx >= 0 && idy >= 0 && idx < N && idy < N) {                   
			// 안파고 가도 되는 경우                                                 
			if (!visited[idx][idy] && map[x][y] > map[idx][idy]) {          
				DFS(idx, idy, cnt + 1, dig); // dig는 변경이 없으니 그대로 넘긴다.       
			}

DFS는 우선 방문 처리를 하고 max와 cnt를 비교해서 갱신을 해준다.

이후에 사방탐색을 돌기 위해 for문을 작성했다.

먼저 범위 안에 있는지 확인하고 조건을 나눠야 한다.

1. 땅을 안 파도 내려갈 수 있는 경우

DFS에 cnt+1과 땅을 파지 않았으니 dig를 그대로 넘긴다. 현재 dig는 false다.

 

 

 

				// 다음 봉우리가 더 높거나 같다면                                                    
				else if (map[x][y] <= map[idx][idy]) {                                  
					// 방문 안 했고, 안 팠고, K만큼 팠을 때 작아지면                                     
					if(!visited[idx][idy] && !dig && map[x][y] > map[idx][idy]-K) {     
						int temp = map[idx][idy]; // 임시로 저장해놔야 돌아올 때 원상복구               
						for(int j=1; j<=K; j++) { // 1부터 K까지 볼 건데                       
							if(map[idx][idy]-j < map[x][y]) { // j를 뺐을 때 map[x][y]가 크면  
								map[idx][idy]-=j; // 빼고                                 
								DFS(idx, idy, cnt + 1, true); // 팠다고 하고 DFS돈다.          
								map[idx][idy] = temp; // 다시 원상복구                        
							}                                                           
						}                                                               
					}                                                                   
				}                                                                       
			}                                                                           
		}                                                                               
		visited[x][y] = false;                                                          
	}                                                                                   
}
2. 다음 봉우리가 더 높은 상태인데 K를 파면 내려갈 수 있는 경우

방문도 하지 않았고, dig==fasle이고, 이동했던 맵에서 K만큼 빼면 이전 맵보다 큰 경우 진행이 가능하다.

우선 맵을 원상복구를 해야하기 때문에 temp에 해당 지점의 값을 저장해놓는다.

바로 K를 빼도 되지만 K미만 뺐을 때 최적이 되는 경우가 있어서 1부터 K까지 전부 살펴보는 방법을 택했다.

j만큼 뺀 다음에 DFS를 true로 바꿔서 넘기면 된다.

!! 다시 되돌아오면 temp에 저장했던 값을 이용해 원상복구를 진행한다. !!

 

그리고 for문이 끝나면 false로 만들어준다.

 

전형적인 DFS문제이다.

반응형