Algorithm/구현(Brute force, Back tracking, etc.)

백준 14889 스타트와 링크

쩡류 2021. 11. 15. 18:24
728x90

https://www.acmicpc.net/problem/14889

 

14889번: 스타트와 링크

예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.

www.acmicpc.net

 

- 난이도가 어렵진 않았지만, 디테일에 신경써야 하는 문제였다.

 

- 역시나 브루트포스 백트래킹 문제이다. 1번부터 최대 20번 선수까지 주어질 때, 각 선수가 A팀에 선택이 되던지, B팀에 선택이 되던지 두 가지의 경우의 수이고, 재귀의 깊이는 20을 넘지 못한다.(한 팀에 과반수의 인원이 들어가는 경우 바로 종결되기 때문.) 따라서, 2^20 이하의 연산량이 되므로 재귀적으로 구현이 가능하다.

 

- 각 팀의 구성원은 vector<int>로 관리하고, start팀에 선수를 넣고 DFS, 호출 종료 후 다시 POP 후 link팀에 선수를 넣고 DFS, 다시 POP하였다.

 

- 모든 팀원이 골고루 start와 link팀에 들어간 경우, 이중 for문을 통해 모든 경우의 수에 대해 능력치들을 더하고 두 팀의 능력치 합을 빼어 answer를 최소값으로 최신화하였다.

 

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;
int n;
int ability[21][21];
int answer = 2100000000;

void DFS(int cnt, vector<int>& start, vector<int>& link) {
	// 팀원이 균일하게 2등분되지 않으므로 종결
	if (start.size() > n / 2 || link.size() > n / 2) return;
	if (cnt > n) {
		int start_sum = 0;
		int link_sum = 0;
		for (int i = 0; i < n / 2; i++) {
			for (int j = i; j < n / 2; j++) {
				start_sum +=( ability[start[i]][start[j]] + ability[start[j]][start[i]]);
			}
		}
		for (int i = 0; i < n / 2; i++) {
			for (int j = i; j < n / 2; j++) {
				link_sum += (ability[link[i]][link[j]] + ability[link[j]][link[i]]);
			}
		}

		answer = min(answer, abs(start_sum - link_sum));
		return;
	}
	start.push_back(cnt);
	DFS(cnt + 1, start, link);
	start.pop_back();
	link.push_back(cnt);
	DFS(cnt + 1, start, link);
	link.pop_back();
}

int main() {
	vector<int> start, link;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			cin >> ability[i][j];
		}
	}
	DFS(1, start, link);
	cout << answer;
	return 0;
}