chldkato

백준 17471 게리맨더링 (파이썬) 본문

백준

백준 17471 게리맨더링 (파이썬)

chldkato 2020. 3. 8. 23:05

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

 

17471번: 게리맨더링

선거구를 [1, 4], [2, 3, 5, 6]으로 나누면 각 선거구의 인구는 9, 8이 된다. 인구 차이는 1이고, 이 값보다 더 작은 값으로 선거구를 나눌 수는 없다.

www.acmicpc.net

구역을 한 개로 나눌 때부터 n//2개로 나눌 때까지 각 경우를 모두 검사한다

n이 5일 때를 예로 들면 구역을 2,3으로 나누는 경우와 3,2로 나누는 경우는 같기 때문에 n//2 까지만 확인하면 된다

 

1. dfs로 선거구를 1개부터 n//2개 까지 뽑는다

2. 목표 개수만큼 지역을 선정하면 각 선거구가 이어져있는지 bfs로 확인한다

3. 이어져있지 않다면 bfs에서 0을 반환하고 이어져있으면 해당 선거구의 인구를 반환한다

4. 인구수 차의 최소값을 계속 기록해나간다

5. 마지막에 조건대로 출력

from collections import deque
import sys

input = sys.stdin.readline

def bfs(g):
    q = deque()
    check = [0 for _ in range(n)]
    q.append(g[0])
    check[g[0]] = 1
    cnt, ans = 1, 0
    while q:
        x = q.popleft()
        ans += people[x]
        for nx in a[x]:
            if nx in g and not check[nx]:
                check[nx] = 1
                cnt += 1
                q.append(nx)
    if cnt == len(g):
        return ans
    else:
        return 0

def dfs(cnt, x, end):
    global min_ans
    if cnt == end:
        g1, g2 = deque(), deque()
        for i in range(n):
            if c[i]:
                g1.append(i)
            else:
                g2.append(i)
        ans1 = bfs(g1)
        if not ans1:
            return
        ans2 = bfs(g2)
        if not ans2:
            return
        min_ans = min(min_ans, abs(ans2 - ans1))
        return

    for i in range(x, n):
        if c[i]:
            continue
        c[i] = 1
        dfs(cnt+1, i, end)
        c[i] = 0

n = int(input())
people = list(map(int, input().split()))

a = [[] for _ in range(n)]
for i in range(n):
    x = list(map(int, input().split()))
    for j in range(1, x[0]+1):
        a[i].append(x[j]-1)

min_ans = sys.maxsize
for i in range(1, n // 2 + 1):
    c = [0 for _ in range(n)]
    dfs(0, 0, i)

if min_ans == sys.maxsize:
    print(-1)
else:
    print(min_ans)

Comments