문제 설명
문제
송도에 사는 상근이와 친구들은 송도에서 열리는 펜타포트 락 페스티벌에 가려고 한다. 올해는 맥주를 마시면서 걸어가기로 했다. 출발은 상근이네 집에서 하고, 맥주 한 박스를 들고 출발한다. 맥주 한 박스에는 맥주가 20개 들어있다. 목이 마르면 안되기 때문에 50미터에 한 병씩 마시려고 한다. 즉, 50미터를 가려면 그 직전에 맥주 한 병을 마셔야 한다.
상근이의 집에서 페스티벌이 열리는 곳은 매우 먼 거리이다. 따라서, 맥주를 더 구매해야 할 수도 있다. 미리 인터넷으로 조사를 해보니 다행히도 맥주를 파는 편의점이 있다. 편의점에 들렸을 때, 빈 병은 버리고 새 맥주 병을 살 수 있다. 하지만, 박스에 들어있는 맥주는 20병을 넘을 수 없다. 편의점을 나선 직후에도 50미터를 가기 전에 맥주 한 병을 마셔야 한다.
편의점, 상근이네 집, 펜타포트 락 페스티벌의 좌표가 주어진다. 상근이와 친구들이 행복하게 페스티벌에 도착할 수 있는지 구하는 프로그램을 작성하시오.
입력
첫째 줄에 테스트 케이스의 개수 t가 주어진다. (t ≤ 50)
각 테스트 케이스의 첫째 줄에는 맥주를 파는 편의점의 개수 n이 주어진다. (0 ≤ n ≤ 100).
다음 n+2개 줄에는 상근이네 집, 편의점, 펜타포트 락 페스티벌 좌표가 주어진다. 각 좌표는 두 정수 x와 y로 이루어져 있다. (두 값 모두 미터, -32768 ≤ x, y ≤ 32767)
송도는 직사각형 모양으로 생긴 도시이다. 두 좌표 사이의 거리는 x 좌표의 차이 + y 좌표의 차이 이다. (맨해튼 거리)
출력
각 테스트 케이스에 대해서 상근이와 친구들이 행복하게 페스티벌에 갈 수 있으면 "happy", 중간에 맥주가 바닥나서 더 이동할 수 없으면 "sad"를 출력한다.
예제
문제 풀이
문제 분석
단순하게 모든 좌표를 노드로 생각하고, 탐색시 1000m(맥주 20개) 이하인 노드를 탐색하며 종점 노드에 도달할 수 있는지 여부를 출력하면 되는 문제로 접근하였습니다.
1
2
0 0
200 600
800 300
1000 1000
인 입력이 있을 때의 그래프를 나타내면 다음과 같습니다.
그림에서는 경우의 수를 위해 화살표로 표현하였지만, 양방향 모두 이동이 가능한 무방향 그래프입니다.
이것을 트리로 나타내면 다음과 같습니다.
index에 관계 없이 종점 노드를 찾을 수 있는지 여부만 출력하면 되기 때문에 DFS탐색을 사용합니다.
<그 외 생각해 볼만한 요소>
1. 거리가 나누어 떨어지지 않는 경우는 생각해야 할까?
문제에서 편의점을 출발할 때 맥주를 바로 마셔야 한다고 명시되어 있습니다. 때문에 편의점을 출발하는 순간은 무조건 20병인 상태로 출발합니다. 그림에서 처럼 초과한 거리 또한 계산하지 않습니다.
2. 탐색시 목표보다 멀어진다면 가지 말아야 할까?
위 그림처럼 목적지에서 멀어지더라도 돌아서 가는 경우도 있기 때문에, 현재 노드보다 목표까지의 거리가 멀어지더라도 계속 탐색하여야 합니다.
정리
- 입력시 모든 노드(좌표)를 배열에 저장한다. 이때 첫 노드는 시작노드, 마지막 노드는 목표 노드이다.
- 시작 노드를 기준으로 탐색을 시작한다. 해당 포스트에서는 DFS 탐색을 수행하였지만 BFS 탐색 또한 가능해 보인다.
- 답을 찾았을 때 더 이상 탐색하지 않기 위해 boolean flag 가 true일 때 return 하도록 한다.
- 목표 지점에 도달하면 flag의 값을 true로 하여 재귀를 탈출하고, flag의 값을 결과 리스트에 저장한다.
- 모든 케이스가 끝나고 flag의 값을 기반으로 답을 출력한다.
입력받기
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
List<Boolean> result = new ArrayList<>();
int t = Integer.parseInt(br.readLine());
for (int i = 0; i < t; i++) {
nodes = new ArrayList<>();
check = new HashSet<>();
flag = false;
int n = Integer.parseInt(br.readLine());
for (int j = 0; j < n + 2; j++) {
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
int tempX = Integer.parseInt(st.nextToken());
int tempY = Integer.parseInt(st.nextToken());
nodes.add(new Info(tempX, tempY));
}
goal = nodes.get(nodes.size() - 1);
DFS(nodes.get(0));
result.add(flag);
nodes.clear();
}
케이스가 여러 번 반복되기 때문에 변수 초기화에 유의하자.
풀이 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static Info goal;
static ArrayList<Info> nodes;
static HashSet<Info> check;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
List<Boolean> result = new ArrayList<>();
int t = Integer.parseInt(br.readLine());
for (int i = 0; i < t; i++) {
nodes = new ArrayList<>();
check = new HashSet<>();
flag = false;
int n = Integer.parseInt(br.readLine());
for (int j = 0; j < n + 2; j++) {
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
int tempX = Integer.parseInt(st.nextToken());
int tempY = Integer.parseInt(st.nextToken());
nodes.add(new Info(tempX, tempY));
}
goal = nodes.get(nodes.size() - 1);
DFS(nodes.get(0));
result.add(flag);
nodes.clear();
}
for (Boolean r : result) {
System.out.println(r ? "happy" : "sad");
}
}
static boolean flag;
private static void DFS(Info currentNode) {
if (flag) return;
if (check.contains(currentNode)) return;
if (currentNode.equals(goal)) {
flag = true;
return;
}
check.add(currentNode);
int x = currentNode.getX();
int y = currentNode.getY();
for (Info toGo : nodes) {
// 현재 거리보다 목표에서 더 멀어지는 경우는 가지 말아야 할까?
// 돌아서 가는 경우도 있지 않을까?
if (toGo.equals(currentNode)) continue;
int toX = toGo.getX();
int toY = toGo.getY();
int length = Math.abs(toX - x) + Math.abs(toY - y);
if (length <= 1000) {
DFS(toGo);
}
}
}
private static class Info {
int x;
int y;
public Info(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
}
회고
여러모로 생각해 볼거리가 많은 문제였습니다. 알고리즘 문제를 풀수록, 문제를 분석하고 방향성을 잡아놓는 것이 중요하다는 것을 느낍니다.
'ALGORITHM' 카테고리의 다른 글
이분탐색 결정 알고리즘 개념정리 및 예제 문제풀이 <마구간 정하기> (2) | 2023.12.14 |
---|---|
백준 14503. 로봇 청소기 문제풀이 자바 JAVA (0) | 2023.12.13 |
백준 2573. 빙산 문제풀이 자바 JAVA (0) | 2023.12.07 |
백준 7569. 토마토 문제풀이 자바 JAVA (1) | 2023.12.07 |
백준 2468. 안전 영역 문제풀이 자바 (JAVA) (1) | 2023.12.07 |