코딩테스트/SWExpert
[코딩테스트] SWEA 1247 최적경로(Java)
[dev] hiro
2025. 6. 30. 02:05
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 1247 최적경로입니다.
포인트
집과 회사의 위치는 고정되어 있고, 중간에 방문해야 하는 고객들의 위치를 어떤 순서로 방문할지 결정하는 것이 핵심 문제입니다.
경로는 다음과 같은 형태로 구성됩니다:
회사 → 고객 1 → 고객 2 → ... → 고객 n → 집
이때, n명의 고객을 어떤 순서로 방문할지 결정하여 전체 경로의 맨해튼 거리 합이 최소가 되도록 해야 합니다.
고객들의 방문 순서를 정하는 모든 경우를 탐색하려면 순열을 고려해야 하며, 이는 시간복잡도 O(n!)의 완전탐색 문제로 볼 수 있습니다.
최악의 경우 n = 10일 때, 총 경우의 수는 10! = 3,628,800이며, 이는 Java 기준으로 일반적인 환경에서 20초 이내에 충분히 처리 가능한 수준입니다.
따라서, n이 10 이하라면 완전탐색 방식으로도 무리가 없습니다.
소스코드
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 Q1247 {
static class Pair {
int x, y;
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
}
private static int testCase, N, ans = Integer.MAX_VALUE;
private static Pair company, house;
private static Pair[] customers;
private static boolean[] visited;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
testCase = Integer.parseInt(br.readLine());
StringBuilder sb = new StringBuilder();
StringTokenizer st;
for (int t = 1; t <= testCase; t++) {
N = Integer.parseInt(br.readLine());
st = new StringTokenizer(br.readLine());
ans = Integer.MAX_VALUE;
customers = new Pair[N];
visited = new boolean[N];
for (int i = 0; i < N + 2; i++) {
if (i == 0) {
company = new Pair(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
} else if (i == 1) {
house = new Pair(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
} else {
customers[i - 2] = new Pair(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
}
}
dfs(new ArrayList<Integer>(), 0);
sb.append("#").append(t).append(" ").append(ans);
if (t != testCase) {
sb.append("\n");
}
}
System.out.println(sb);
}
private static void dfs(List<Integer> orders, int cnt) {
if (cnt == N) {
int sum = 0;
sum = getSum(orders, sum);
ans = Math.min(ans, sum);
return;
}
for (int i = 0; i < N; i++) {
if (visited[i]) continue;
visited[i] = true;
orders.add(i);
dfs(orders, cnt + 1);
orders.remove(orders.size() - 1);
visited[i] = false;
}
}
private static int getSum(List<Integer> orders, int sum) {
Pair cur = company;
for (int nextIdx : orders) {
Pair next = customers[nextIdx];
sum += cal(cur, next);
cur = next;
}
sum += Math.abs(cur.x - house.x) + Math.abs(cur.y - house.y);
return sum;
}
private static int cal(Pair cur, Pair next) {
return Math.abs(cur.x - next.x) + Math.abs(cur.y - next.y);
}
}
/**
3
5
0 0 100 100 70 40 30 10 10 5 90 70 50 20
6
88 81 85 80 19 22 31 15 27 29 30 10 20 26 5 14
10
39 9 97 61 35 93 62 64 96 39 36 36 9 59 59 96 61 7 64 43 43 58 1 36
*/
코드 설명
- 집, 회사, 고객을 Pair 클래스로 초기화합니다.
- dfs(List<Integer> orders, int cnt)
고객의 방문 순서 = orders, cnt = 현재까지 고려한 고객의 수- 고객을 방문할 때마다 orders에 추가하고, visited 배열로 방문 여부 체크합니다.
- 이후 dfs(orders, cnt+1)로 재귀 호출을 진행합니다.
- 고객 수 cnt == n이면, 현재 순서대로 이동 거리 총합을 계산합니다