문제 : 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부터 짝수인 n까지의 사람이 있다.
팀을 n/2, n/2명으로 두 팀으로 나눈다.
이때, 각 팀원들의 능력치를 합산해서 두 팀의 최종 능력치를 구한다.
그다음, 두 팀의 최종 능력치가 최솟값을 구하면 된다.
접근 방법
팀원들을 나누는 방법은 조합을 사용해서 가능한 모든 경우의 수를 탐색하면 된다.
n명 중 n/2명을 뽑으면 나머지 인원들이 다른 팀이 되기 때문이다.
int n;
int arr[20 + 1][20 + 1];
int minNum = 10001;
n이 최대 20까지 입력 가능하니 2차원 배열의 크기를 21 * 21로 해주었다.
minNum은 두 팀의 차이를 저장할 것이다.
최악의 경우 한 팀의 10명이 모두 100이고 다른 팀의 10명이 모두 1일 경우 차이가 최대가 된다.
그래서 대충 10001로 지정해주었다.
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> arr[i][j];
}
}
2차원 배열의 값을 입력받는다.
vector<int> v;
for (int i = 1; i <= n; i++) {
v.push_back(i);
}
1부터 n까지의 사람들의 번호를 v 벡터에 저장한다
vector<int> temp;
int k = n / 2;
for (int i = 0; i < k; i++) {
temp.push_back(1);
}
for (int i = 0; i < v.size() - k; i++) {
temp.push_back(0);
}
sort(temp.begin(), temp.end());
조합을 사용하기 위한 temp벡터이다.
k가 뽑는 수이기 때문에 n/2로 초기화해준다.
그리고 next_permutaion함수를 통해 조합의 모든 경우의 수를 탐색할 것이다.
do {
vector<int> start;
vector<int> link;
int startTotal = 0;
int linkTotal = 0;
for (int i = 0; i < temp.size(); i++) {
if (temp[i] == 1) {
start.push_back(v[i]);
} else {
link.push_back(v[i]);
}
}
for (int i = 0; i < start.size(); i++) {
for (int j = i + 1; j < start.size(); j++) {
startTotal += arr[start[i]][start[j]];
startTotal += arr[start[j]][start[i]];
}
}
for (int i = 0; i < link.size(); i++) {
for (int j = i + 1; j < link.size(); j++) {
linkTotal += arr[link[i]][link[j]];
linkTotal += arr[link[j]][link[i]];
}
}
int diff = abs(linkTotal - startTotal);
if(diff < minNum){
minNum = diff;
}
if(minNum==0){
break;
}
} while (next_permutation(temp.begin(), temp.end()));
스타트팀과 링크팀 팀원의 숫자를 저장할 벡터를 선언하고 각 팀의 능력치를 저장할 변수를 0으로 초기화해준다.
이제 조합을 통해 팀을 분류해준다.
temp의 인덱스 값이 1이면 스타트팀, 0이면 링크팀으로 추가해준다.
그리고 각 팀의 점수를 이중 for문을 통해 계산해준다.
코드를 보면 이해가 될 것이다.
그리고 두 팀의 능력치 차이의 절댓값(diff)과 minNum을 비교한다.
만약 diff가 minNum보다 작다면 minNum을 diff로 바꿔준다.
하지만 여기서 minNum이 0이라면 가장 작은 차이이니 남은 경우의 수를 더 알아볼 필요가 없으므로 반복문을 탈출한다.
cout << minNum;
계산된 두 팀의 능력치 차이의 최솟값을 출력해주면 끝!
무엇이 어려웠는가?
조합을 사용할 줄 알았다면 문제의 로직 자체에는 큰 어려움이 없었다.
다만 구현하는 부분이 조금 번거로웠다.
사실 모든 경우의 수를 다 탐색해볼 필요는 없다.
n이 4일 때, 즉 사람이 4명일 경우이다.
3 4
2 4
2 3
1 4
1 3
1 2
3, 4 일 때는 1, 2가 반대
2, 4 일 때는 1, 3이 반대
.
.
.
보듯이 i번째와 n-i번째가 같은 경우이다.
그래서 nC(n/2)의 값의 반만 탐색하면 된다.
nC(n/2)의 반만큼만 반복하려고 했는데,
nC(n/2)의 값을 따로 구하기 싫었다.
시간도 충분하고 n의 크기도 크지 않아서 그냥 모든 경우의 수를 중복되더라고 탐색하기로 했다.
배운점
완전탐색 재밌다!
모든 경우를 다 봐야하니 특별한 예외처리도 없고 맘에 든다 너!
'백준 문제풀이(BOJ PS)' 카테고리의 다른 글
[백준(BOJ)] 2839번 설탕 배달 C++(다이나믹 프로그래밍) (0) | 2022.08.13 |
---|---|
[백준(BOJ)] 2193번 이친수 C++ (0) | 2022.08.13 |
[백준(BOJ)] 18111번 마인크래프트 C++ (0) | 2022.08.09 |
[백준(BOJ)] 14888번 연산자 끼워넣기 C++ (0) | 2022.08.09 |
[백준(BOJ)] 6603번 로또 C++ (0) | 2022.08.09 |