문제 링크는 여기.
N명의 인원을 두 팀으로 분리한다. 이때 시너지 표를 참고하여 두 팀의 시너지 점수 차이의 최소 값을 구하면 된다.
각 팀의 시너지 점수 구하는 방법은 문제에 잘 나와있다.

첫 번째로 해야하는 것은 팀 나누기이다. 이때 팀은 조합을 통해 쉽게 구할 수 있다.
한 팀당 인원은 N/2명인 것을 고려하여 조합 메서드는 아래와 같이 작성하였다.
public static int comp(int i, int idx, int N, int[][] S, boolean[] cnt) {
// 팀을 다 정했으면 해당 팀인 경우 시너지 차이 반환
if(idx==N/2) return compute(N, S, cnt);
// 모든 경우 중 최소값 반환을 위해 초기 result은 Max값으로 설정
int result = Integer.MAX_VALUE;
// 조합이기 때문에 i는 인자로 받은 것을 그대로 반복문에 사용했음
for(; i<N; i++) {
cnt[i] = true;
// 선택한 경우의 시너지 차이 값 받고 최소값을 선택해 저장
result = Math.min(result, comp(i+1, idx+1, N, S, cnt));
cnt[i] = false;
}
return result;
}
이어서 팀이 결정된 이후의 시너지 연산 메서드는 다음과 같다.
private static int compute(int N, int[][] S, boolean[] cnt) {
// 어쩌피 두 팀의 시너지 차이를 구하는 것이므로 temp에 더할 때 팀에 따라 부호를 달리하여
// 처리하였음
int result = 0;
for(int r=0; r<N; r++) {
int temp = 0;
for(int c=0; c<N; c++) {
if(cnt[r]==cnt[c]) temp+=S[r][c];
}
if(cnt[r]) result += temp;
else result -= temp;
}
// 반환값이 음수일 수 있으므로 절댓값 씌움
return Math.abs(result);
}
최종 제출 코드는 다음과 같다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
// 입력 처리부
int N = Integer.parseInt(br.readLine());
int[][] S = new int[N][N];
boolean[] cnt = new boolean[N];
for(int r=0; r<N; r++) {
st = new StringTokenizer(br.readLine());
for(int c=0; c<N; c++) {
S[r][c] = Integer.parseInt(st.nextToken());
}
}
// 실제 연산부
System.out.println(comp(0, 0, N, S, cnt));
}
public static int comp(int i, int idx, int N, int[][] S, boolean[] cnt) {
if(idx==N/2) return compute(N, S, cnt);
int result = Integer.MAX_VALUE;
for(; i<N; i++) {
cnt[i] = true;
result = Math.min(result, comp(i+1, idx+1, N, S, cnt));
cnt[i] = false;
}
return result;
}
private static int compute(int N, int[][] S, boolean[] cnt) {
int result = 0;
for(int r=0; r<N; r++) {
int temp = 0;
for(int c=0; c<N; c++) {
if(cnt[r]==cnt[c]) temp+=S[r][c];
}
if(cnt[r]) result += temp;
else result -= temp;
}
return Math.abs(result);
}
}
제출 결과는 다음과 같았다.

첫번째 시간초과의 경우 버릇처럼 조합이 아닌 순열로 코드를 구성해서 발생했다..
확실히 순열, 조합, 부분집합 코드 연습을 하고 나니 이를 활용할 수 있는 문제가 나오면 굉장히 반갑기도 하고, 좀 더 쉽게 느껴지는 것 같다!