프로그래밍/알고리즘 풀이
[백트래킹] 스타트와 링크 (백준 14889번)
카카수(kakasoo)
2020. 3. 1. 22:55
반응형
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
|
#include <iostream>
using namespace std;
int startLink[21];
int arr[21][21];
int visited[21];
int n;
void dfs(int cnt, int start)
{
if (cnt == n / 2)
{
for (int i = 0; i < n / 2; i++)
cout << startLink[i] << ' ';
cout << endl;
return;
}
for (int i = start + 1; i <= n; i++)
{
if (!visited[i])
{
visited[i] = true;
startLink[cnt] = i;
dfs(cnt + 1, i);
visited[i] = false;
}
}
}
int main(void)
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> arr[i][j];
dfs(0, 0);
}
|
실행 결과는 아래와 같다.

내용이 길어서 잘리긴 했지만, 만약 n = 8인 형태의 arr를 입력하게 된다면,
중복이 없는 오름차순으로 1 2 3 4 부터 5 6 7 8 까지 출력한다.
이렇게 만든 이유는, 만약 n/2 (이 경우에는 4)의 값만큼 startTeam에 넣을 수 있다면,
나머지는 자동으로 linkTeam이 될 것이기 때문이다.
아래는 위의 dfs 함수를 수정하여 만든 정답이다.
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
|
#include <iostream>
using namespace std;
int startLink[21];
int arr[21][21];
int visited[21];
int n;
int minV = 987654321;
void dfs(int cnt, int start)
{
if (cnt == n /2 )
{
int sumA = 0, sumB = 0;
for (int i = 0; i < n / 2; i++)
{
for (int j = i + 1; j < n / 2; j++)
{
sumA += (arr[startLink[i]][startLink[j]] + arr[startLink[j]][startLink[i]]);
}
}
// sumA는 start팀의 점수를 구하는 것.
int link[21];
// link 팀의 점수를 구하기 위해서 link팀 배열을 만들고, start 팀에 없는 사람을 넣는다.
// 아래의 반복문이 cnt가 n/2인 시점에서 방문하지 않은 곳들을 찾아 배열에 대입해준다.
int cur = 0;
for (int i = 1; i <= n; i++)
{
if (!visited[i])
link[cur++] = i;
}
for (int i = 0; i < n / 2; i++)
{
for (int j = i + 1; j < n / 2; j++)
{
sumB += (arr[link[i]][link[j]] + arr[link[j]][link[i]]);
}
}
// 마찬가지로 sumB는 link팀의 점수를 구하는 것.
if (minV > abs(sumA - sumB)) minV = abs(sumA - sumB);
// if (minV == 0) { cout << 0; return 0;} 로 끝내버려도 된다.
return;
}
for (int i = start + 1; i <= n; i++)
{
if (!visited[i])
{
visited[i] = true;
startLink[cnt] = i;
dfs(cnt + 1, i);
visited[i] = false;
}
}
// 전형적인 n과 m 코드다.
// dfs의 매개변수로는 현재 start팀에 입력된 멤버의 숫자와 탐색 시작점이다.
// dfs의 두번째 매개변수로 i를 넣은 것을 볼 수 있을 텐데,
// 이 두번째 매개변수 덕분에 오름차순으로 삽입이 진행된다.
}
int main(void)
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> arr[i][j];
dfs(0, 0);
cout << minV;
}
|
느낀점
풀다가 죽을 뻔 했는데, n과 m 문제가 생각난 것이 신의 한수였다.
dfs임에도 계속 중복이 나왔던 이유를 다음과 같이 생각해볼 수 있겠다.
1) dfs를 2갈래 나눠서 start팀에 대입한 경우와 link팀에 대입한 경우를 구한 것, 결국 분화된다.
2) 시작점을 따로 명시하지 않은 점. (1 2 3 4를 7~8번 정도 하여 시간초과 발생.)
해결책은 dfs식을 하나로 통합하고, 탐색 시작 지점을 dfs의 두번째 매개변수로 전달한 것.
신의 한수였다고 잘도 말했지만, 정말로 머리에 한계가 왔다.
지금의 이 짜릿함이 없다면, 내일 아마 코딩에 손도 못댔을 것이다.
문제를 풀고자 지금까지 매달린 게 다행이다, 코딩에 정 떨어지기 전에 오늘은 빨리 관두자.
반응형