[백준 Python] 14889번 스타트와 링크 (백트래킹)

728x90
반응형

백준 14889번 스타트와 링크 (백트래킹)

1. 문제

백준 14889번: 스타트와 링크

두 팀으로 나누어 능력치의 합을 비교하는 문제이다. (n)명이 주어지면 이를 절반으로 나누어 두 팀을 구성하고, 두 팀의 능력치 차이를 최소화하는 것이 목표이다.

팀의 능력치는 각 팀에 속한 두 사람의 능력치의 합으로 계산된다. 즉, 두 사람이 같은 팀에 속했을 때 (S_{ij} + S_{ji})가 팀의 능력치로 더해진다.


2. 풀이

이 문제는 백트래킹을 이용해 모든 경우의 수를 탐색해서 최적의 값을 찾아야 하는 문제이다.

주어진 사람들을 두 팀으로 나누는 방법을 탐색하면서 팀의 능력치 차이를 계산한다.

문제 풀이 방법

  1. 백트래킹 함수 정의
    • 사람들을 하나씩 선택하며 두 팀으로 나누는 과정을 탐색한다.
    • (visited) 배열을 이용해 어떤 사람이 어느 팀에 포함되었는지 체크한다.
  2. 팀의 능력치 계산 함수 정의
    • 주어진 팀을 인자로 받아서 팀원들 간의 모든 능력치 합을 계산한다.
    • 모든 가능한 두 사람의 조합을 찾아서, 능력치를 더하는 방식으로 구현한다.
  3. 최솟값 비교 및 업데이트
    • 모든 경우의 수를 탐색하면서 능력치 차이를 계산하여 최솟값을 업데이트한다.

3. 코드

import sys
input = sys.stdin.readline

n = int(input().rstrip())
s = [list(map(int, input().rstrip().split())) for _ in range(n)]

min_diff = float('inf')
team_size = n//2
visited = [False] * n

def teamPower(team):
    power = 0
    for i in range(len(team)):
        for j in range(i+1, len(team)):
            power += s[team[i]][team[j]] + s[team[j]][team[i]]
    return power

def backtrack(idx, cnt):
    global min_diff

    if cnt == team_size:
        team1, team2 = [], []

        for i in range(n):
            if visited[i]:
                team1.append(i)
            else:
                team2.append(i)

        power1 = teamPower(team1)
        power2 = teamPower(team2)

        min_diff = min(min_diff, abs(power1 - power2))
        return

    for i in range(idx, n):
        if not visited[i]:
            visited[i] = True
            backtrack(i + 1, cnt + 1)
            visited[i] = False

backtrack(0, 0)
print(min_diff)

 

 

입력 받기

n = int(input().rstrip())
s = [list(map(int, input().rstrip().split())) for _ in range(n)]
  • (n): 사람의 수.
  • (s): 능력치 표. (s[i][j])는 사람 (i)와 (j)가 같은 팀일 때의 능력치.

 

팀 나누기

여기서는 visited 배열을 사용하여 팀을 나눈다. True는 팀1에 포함된 사람을, False는 팀2에 포함된 사람을 의미한다.

  • visited 배열을 사용해서 팀에 포함된 사람을 표시한다. (True면 팀1에 속한 사람, False면 팀2에 속한 사람)
  • backtrack 함수는 재귀적으로 호출되며, 각 사람을 팀1에 넣거나 넣지 않는 방식으로 모든 경우의 수를 탐색한다.
  • cnt 변수를 이용해 현재 팀1에 포함된 사람의 수를 체크하고, 팀1의 인원이 n//2명이 되면 팀 나누기를 종료한다.
  • 팀1과 팀2를 나눈 뒤, 각각의 능력치를 계산하고 차이를 비교하여 최솟값을 갱신한다.
team1, team2 = [], []

for i in range(n):
    if visited[i]:
        team1.append(i)
    else:
        team2.append(i)

쉽게 말하면, 모든 사람을 한 명씩 팀1에 넣어보고 다시 빼면서 경우의 수를 모두 확인하는 방식이다.

 

팀의 능력치 계산 함수

def teamPower(team):
    power = 0
    for i in range(len(team)):              # i는 현재 사람의 인덱스
        for j in range(i + 1, len(team)):   # j는 i보다 큰 인덱스만 탐색 (중복 방지)
            power += s[team[i]][team[j]] + s[team[j]][team[i]]
    return power
  • 팀의 모든 가능한 두 사람의 조합에 대해 (s[i][j] + s[j][i]) 값을 더하여 능력치를 계산한다.
S[3][4] + S[4][3] = 4 + 4 = 8
S[3][5] + S[5][3] = 5 + 4 = 9
S[4][5] + S[5][4] = 5 + 5 = 10
합계: 8 + 9 + 10 = 27

 

백트래킹 함수

def backtrack(idx, cnt):
    global min_diff
    if cnt == team_size:
        team1, team2 = [], []
        for i in range(n):
            if visited[i]:
                team1.append(i)
            else:
                team2.append(i)
        power1 = teamPower(team1)
        power2 = teamPower(team2)
        min_diff = min(min_diff, abs(power1 - power2))
        return
    for i in range(idx, n):
        if not visited[i]:
            visited[i] = True
            backtrack(i + 1, cnt + 1)
            visited[i] = False
  • 현재 인덱스 (i)에서 사람을 선택하거나 선택하지 않는 방식으로 모든 경우를 탐색한다.
  • 선택된 사람들로 팀을 구성한 후, (teamPower()) 함수로 능력치를 계산하여 최소 차이를 업데이트한다.

탐색 트리 예시

0 선택 → 1 선택 → 2 선택 → [0,1,2] vs [3,4,5]
                     ↳ 3 선택 → [0,1,3] vs [2,4,5]
                     ↳ 4 선택 → [0,1,4] vs [2,3,5]
                     ↳ 5 선택 → [0,1,5] vs [2,3,4]

        ↳ 2 선택 → 3 선택 → [0,2,3] vs [1,4,5]
                     ↳ 4 선택 → [0,2,4] vs [1,3,5]
                     ↳ 5 선택 → [0,2,5] vs [1,3,4]
                     
        ↳ 3 선택 → 4 선택 → [0,3,4] vs [1,2,5]
                     ↳ 5 선택 → [0,3,5] vs [1,2,4]
                     
        ↳ 4 선택 → 5 선택 → [0,4,5] vs [1,2,3]

 

4. 결과


💡 핵심 포인트

  • 사람들을 두 팀으로 나누는 모든 경우의 수를 탐색하기 위해 백트래킹을 사용한다.
  • 방문 체크 배열 (visited)을 사용하여 팀을 구분한다.
  • 팀의 능력치 계산 함수는 (O(n^2))의 시간 복잡도를 가진다. (모든 두 사람의 조합을 확인하므로)
  • 팀이 구성된 후 능력치 차이를 비교하여 최소값을 업데이트한다.

이 문제의 핵심은 모든 경우의 수를 탐색하되, 가지치기(pruning)를 잘하는 것이다. 백트래킹을 활용해서 효율적으로 풀 수 있다.

728x90
반응형