[Java] 백준(실버-2) 14889번 - 스타트와 링크
문제 풀이
이 문제는 재귀를 통한 백트래킹 유형으로 풀 수 있는 문제 중 하나이다.
아이디어 도출
N이 4일 때, 즉 4명의 사람이 있을 경우 2팀으로 나누어 조합을 톨해 경우의 수를 구해야 한다. 그 경우의 수는 다음과 같다.
1
2
3
[(1, 2), (3, 4)],
[(1, 3), (2, 4)],
[(1, 4), (2, 3)]
이를 통해 구할 수 있는 스타트 팀과 링크 팀의 능력치를 다음과 같이 구할 수 있다.
1
2
3
[5(S12 + S21), 7(S34, S43)] = 2,
[9(S13, S31), 10(S24, S42)] = 1,
[6(S14, S41), 6(S23, S32)] = 0
위 조합에서 팀 간의 점수 차가 가장 낮은 방법은 1번과 4번이 한 팀, 2번과 3번이 한 팀이었을 때이다! 이와 같이 모든 조합의 경우의 수를 탐색해가며 두 팀 간 능력치의 차가 최솟값이 되는 조합 방법을 찾으면 된다.
문제풀이를 위한 아이디어는 다음과 같다.
- N/2 만큼 사람을 구분하여 팀을 나누기 때문에, 재귀 호출을 하며 재귀의 깊이가 N/2가 되면 종료한다.
- 방문 배열에 방문한 노드의 페어로 같은 팀을 구분하여 스타트팀과 링크 팀의 점수를 구한다.
- 두 팀의 점수차의 절댓값을 구한 후 출력한다.
두 팀의 점수차가 0점이라면 이미 최소이기에 탐색을 더 진행할 필요가 없다.
문제 풀이를 위해 작성한 코드는 아래와 같다.
작성코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import java.io.*;
import java.util.*;
class Main {
// 방문배열과 입력배열 선언
static boolean[] visited;
static int[][] arr;
// 배열의 크기 N 선언
static int N;
// 최솟값을 담을 변수 선언
static int min;
public static void main(String[] args) throws IOException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out))) {
N = Integer.parseInt(br.readLine());
arr = new int[N][N];
visited = new boolean[N];
for(int i=0; i<N; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
for(int j=0; j<N; j++) {
arr[i][j] = Integer.parseInt(st.nextToken());
}
}
// 최솟값 갱신을 위한 min 초기화.
min = Integer.MAX_VALUE;
// dfs 함수 호출
dfs(0, 0);
bw.write(min+"\n");
}
}
/**
* idx: 인덱스
* depth: 재귀의 깊이, 조합 개수
*/
public static void dfs(int idx, int depth) {
// N/2 번만큼 반복후 종료한다.
if(depth == N / 2) {
// 스타트 팀과 링크 팀의 점수를 구하기 위한 초기화.
int start = 0;
int link = 0;
// 방문한 팀과 방문하지 않은 팀을 나누어 팀의 점수를 구하고 최솟값을 찾는다.
for(int i=0; i<N-1; i++) {
for(int j=i+1; j<N; j++) {
// i번째와 j번째 사람이 true라면, start team이다.
if(visited[i] == true && visited[j] == true) {
start += arr[i][j];
start += arr[j][i];
}
// i번째와 j번째 사람이 fals라면, link team이다.
else if(visited[i] == false && visited[j] == false) {
link += arr[i][j];
link += arr[j][i];
}
}
}
// 두 팀의 점수 차이 절댓값을 구한다.
int result = Math.abs(start - link);
// 만약 두 팀의 점수 차가 0이라면 가장 최솟값이기에 바로 죵료한다.
if(result == 0) {
min = 0;
return;
}
min = Math.min(min, result);
}
// N까지 반복해가며 방문하지 않았다면 아래 내용 수행
// 방문 처리 -> 재귀 호출 -> 방문 처리 해제
for(int i=idx; i<N; i++) {
if(!visited[i]) {
visited[i] = true;
dfs(i+1, depth+1);
visited[i] = false;
}
}
}
}
출처
- 해당 문제의 저작권은 문제를 만든이에게 있으며 자세한 내용은 문제 링크에서 참조바랍니다.