등산로를 조성하려고 한다.
등산로를 만들기 위한 부지는 N * N 크기를 가지고 있으며, 이곳에 최대한 긴 등산로를 만들 계획이다.
등산로 부지는 아래 [Fig. 1]과 같이 숫자가 표시된 지도로 주어지며, 각 숫자는 지형의 높이를 나타낸다.
등산로를 만드는 규칙은 다음과 같다.
① 등산로는 가장 높은 봉우리에서 시작해야 한다.
② 등산로는 산으로 올라갈 수 있도록 반드시 높은 지형에서 낮은 지형으로 가로 또는 세로 방향으로 연결이 되어야 한다.
즉, 높이가 같은 곳 혹은 낮은 지형이나, 대각선 방향의 연결은 불가능하다.
③ 긴 등산로를 만들기 위해 딱 한 곳을 정해서 최대 K 깊이만큼 지형을 깎는 공사를 할 수 있다.
N * N 크기의 지도가 주어지고, 최대 공사 가능 깊이 K가 주어진다.
이때 만들 수 있는 가장 긴 등산로를 찾아 그 길이를 출력하는 프로그램을 작성하라.
[예시]
위 [Fig. 1]과 같이 N = 5인 지도가 주어진 경우를 살펴보자.
가장 높은 봉우리는 높이가 9로 표시된 세 군데이다.
이 세 곳에서 출발하는 가장 긴 등산로 중 하나는 아래 [Fig. 2]와 같고, 이 때 길이는 5가 된다.
만약 최대 공사 가능 깊이 K = 1로 주어질 경우,
아래 [Fig. 3]과 같이 빨간색 부분의 높이를 9에서 8로 깎으면 길이가 6인 등산로를 만들 수 있다.
이 예에서 만들 수 있는 가장 긴 등산로는 위와 같으며, 출력할 정답은 6이 된다.
[제약 사항]
1. 시간 제한 : 최대 51개 테스트 케이스를 모두 통과하는 데 C/C++/Java 모두 3초
2. 지도의 한 변의 길이 N은 3 이상 8 이하의 정수이다. (3 ≤ N ≤ 8)
3. 최대 공사 가능 깊이 K는 1 이상 5 이하의 정수이다. (1 ≤ K ≤ 5)
4. 지도에 나타나는 지형의 높이는 1 이상 20 이하의 정수이다.
5. 지도에서 가장 높은 봉우리는 최대 5개이다.
6. 지형은 정수 단위로만 깎을 수 있다.
7. 필요한 경우 지형을 깎아 높이를 1보다 작게 만드는 것도 가능하다.
[입력]
입력의 맨 첫 줄에는 총 테스트 케이스의 개수 T가 주어지고, 그 다음 줄부터 T개의 테스트 케이스가 주어진다.
각 테스트 케이스의 첫 번째 줄에는 지도의 한 변의 길이 N, 최대 공사 가능 깊이 K가 차례로 주어진다.
그 다음 N개의 줄에는 N * N 크기의 지도 정보가 주어진다.
[출력]
테스트 케이스 개수만큼 T개의 줄에 각각의 테스트 케이스에 대한 답을 출력한다.
각 줄은 "#t"로 시작하고 공백을 하나 둔 다음 정답을 출력한다. (t는 1부터 시작하는 테스트 케이스의 번호이다)
출력해야 할 정답은 만들 수 있는 가장 긴 등산로의 길이이다.
🌈 풀이 후기
- dfs가 응용된 버전이다.
- 최고 봉우리는 선택을 하고 k를 깍아야 마지막 테스트 케이스를 통과할 수 있다... 문제 설명이 조금 애매한듯..
- 최고 봉우리를 미리 선택한 다음에 모든 경우의 대해서 k를 0부터 K까지 깍아보고 dfs를 돌리면 된다.
- 어차피 작은쪽으로만 가기 때문에 방문처리가 필요없는데 굳이 방문처리 하다가 여러번 틀렸다.. 조심하자!
👩🏫 문제 풀이
- 최고 높이의 봉우리를 구해서 list에 저장한다.
- 모든 경우에 대해서 k를 0~K까지 깍아보고 모든 경우의 수를 dfs로 돌린다.
- 매 경우 max를 구해준다.
package SWTest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class SWEA_SWTEST_등산로조성 {
static int N, K;
static int[][] map;
static int[] dr = { -1, 1, 0, 0 };
static int[] dc = { 0, 0, -1, 1 };
static int max = 0;
static int maxV = 0;
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine());
StringTokenizer st;
for (int tc = 1; tc <= T; tc++) {
st = new StringTokenizer(br.readLine(), " ");
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
map = new int[N][N];
maxV = 0;
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine(), " ");
for (int j = 0; j < N; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
maxV = Math.max(maxV, map[i][j]);
}
}
max = 0;
List<Point> list = new ArrayList<>();
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (map[i][j] == maxV) {
// 최고 봉우리 저장
list.add(new Point(i, j));
}
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k <= K; k++) {
map[i][j] -= k;
for (int h = 0; h < list.size(); h++) {
dfs(1, list.get(h).r, list.get(h).c);
}
map[i][j] += k;
}
}
}
System.out.println("#" + tc + " " + max);
}
}
private static void dfs(int cnt, int r, int c) {
for (int d = 0; d < 4; d++) {
int nr = r + dr[d];
int nc = c + dc[d];
if (nr >= 0 && nr < N && nc >= 0 && nc < N) {
if (map[nr][nc] < map[r][c]) {
dfs(cnt + 1, nr, nc);
}
}
}
max = Math.max(cnt, max);
}
static class Point {
int r, c;
public Point(int r, int c) {
super();
this.r = r;
this.c = c;
}
}
}
업그레이드 버전
1. K는 1만 깍아도 된다.
2. 그렇기에 k for문을 굳이 반복하지 않아도 된다.
package swea;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class SWEA1949_등산로조성 {
static int N, K, map[][], H, max, before;
static int dr[] = { 0, 1, 0, -1 };
static int dc[] = { 1, 0, -1, 0 };
static boolean visited[][];
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
StringBuilder sb = new StringBuilder();
int T = Integer.parseInt(br.readLine());
for (int tc = 1; tc <= T; tc++) {
max = 0;
H = 0;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
map = new int[N][N];
visited = new boolean[N][N];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
if (map[i][j] > H)
H = map[i][j];
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (map[i][j] == H) {
search(i, j, false, 1);
}
}
}
sb.append('#').append(tc).append(' ').append(max).append('\n');
}
System.out.println(sb);
}
// 진입한 칸의 좌표, 깎음 여부, 이전까지의 등산로 길
private static void search(int r, int c, boolean l, int s) {
visited[r][c] = true;
for (int i = 0; i < 4; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
if (nr < 0 || nr >= N || nc < 0 || nc >= N || visited[nr][nc])
continue;
if (map[nr][nc] < map[r][c])
search(nr, nc, l, s + 1);
else if (map[nr][nc] - K < map[r][c] && !l) {
before = map[nr][nc];
map[nr][nc] = map[r][c] - 1;
search(nr, nc, true, s + 1);
map[nr][nc] = before;
l = false;
}
}
visited[r][c] = false;
if (s > max)
max = s;
}
}
'Algorithm(알고리즘) > SWEA(SW Expert Academy)' 카테고리의 다른 글
[SWEA][Java][모의 SW][재귀, 부분집합] 2112 - 보호 필름 (0) | 2021.04.16 |
---|---|
[SWEA][Java][모의 SW][DFS] 2105 - 디저트 카페 (0) | 2021.04.16 |
[SWEA][Java][D4][최소 신장 트리,크루스칼] 1251 - 하나로 (0) | 2021.03.29 |
[SWEA][Java][D3][시물레이션] 1873 - 상호의 배틀필드 (0) | 2021.02.03 |
[SWEA][Java][D2] 1204 - [S/W 문제해결 기본] 1일차 - 최빈수 구하기 (0) | 2021.01.26 |