오늘 풀어본 문제는 ⭐스타트와 링크라는 문제이다.
즉, 다음은 i가 포함된 모든 i↔j 관계의 총합.
(Sij, Sji가 중복으로 들어갔음)
rowSum[i] = Σ S[i][j]
colSum[i] = Σ S[j][i]
weight[i] = rowSum[i] + colSum[i]
📍모든 능력치의 총합
totalSum <= Σ S[i][j] (모든 i,j)
dfs(1, 1, total - rowSum[1] - colSum[1]);
static void dfs(int cnt, int last, int sum) {
if (cnt == N / 2) {
//rowSum+colSum은 중복된 값이 포함된 수임
//즉 total에서 해당 값을 빼도 음수 나올 수 있음
ANSWER = Math.min(ANSWER, Math.abs(sum));
return;
}
if (N - last < N / 2 - cnt) {
return;
}
for (int i = last + 1; i <= N; i++) {
dfs(cnt + 1, i, sum - rowSum[i] - colSum[i]);
}
}
쌍의 합을 이용하는 게 아닌, 조합을 만들어 문제를 해결하는 방법이다.
select[1] = true; // 1번 사람을 팀에 고정 -> 대칭성 경우의 수 제거
dfs(2, 1);
System.out.println(ANSWER);
}
static void dfs(int idx, int cnt) {
if (cnt == N/2) {
calc();
return;
}
if (idx > N) return;
if (N - idx + 1 < N/2 - cnt) return;
select[idx] = true;
dfs(idx + 1, cnt + 1);
select[idx] = false;
dfs(idx + 1, cnt);
}
static void calc() {
int A = 0, B = 0;
for (int i = 1; i <= N; i++) {
for (int j = i + 1; j <= N; j++) {
if (select[i] && select[j]) {
A += S[i][j] + S[j][i];
} else if (!select[i] && !select[j]) {
B += S[i][j] + S[j][i];
}
}
}
ANSWER = Math.min(ANSWER, Math.abs(A - B));
}
❓처음 코드를 짤 때, 3.2번의 과정 속 dfs에서 for문을 하나 더 사용했었다.
다음과 같은 느낌이었다.
for (int i = idx; i <= N; i++) {
select[i] = true;
dfs(idx + 1, cnt + 1);
select[i] = false;
dfs(idx + 1, cnt);
}
이는 왜 잘못된 판단이었을까?
사람 1 → 넣을지 말지
사람 2 → 넣을지 말지
사람 3 → 넣을지 말지
👉 각 단계에서 “후보가 딱 1명” → for문 필요 없음!
‼️위와 같은 코드가 필요한 경우는? → 이번 단계에서 여러 명 중 하나를 고르는 구조!
ex1) “n개 중 r개를 골라라”
ex2) “n개 중 정확히 k개 골라라”
ex3) “지금 위치에 올 수 있는 수가 여러 개”