STUDY/Algorithm

[백준] 17471 게리맨더링 python

sinawi95 2022. 3. 7. 10:09
728x90

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

 

17471번: 게리맨더링

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

www.acmicpc.net

백트래킹과 그래프 탐색이 합쳐져 있어서 뭔가 삼성 기출 같은 문제이다. 

이 문제는 선거구를 두 지역으로 나누고(조합,  backtracking) 선거구끼리 연결되어있는지 확인해서(graph traversal, bfs, dfs) 연결되어있는 경우 최솟값이 되는지 확인하면 된다.

 

 

# import sys; input = sys.stdin.readline
from collections import deque

def check(limit):
    global population, graph, answer, visit
    ret = [0, 0, 0] # T/F, area1, area2
    # 선거구가 한쪽에 몰려있는 경우
    if 0 == sum(visit) or limit == sum(visit):
        return ret

    # 선거구끼리 연결이 되어있지 않은 경우 탐색
    area1 = set()
    area2 = set()
    for i, v in enumerate(visit):
        if i == 0: continue
        if v:
            area1.add(i)
            ret[1] += population[i]
        else:
            area2.add(i)
            ret[2] += population[i]
    visit_area1 = [0 for _ in range(len(visit))]
    visit_area2 = [0 for _ in range(len(visit))]
    q = deque()
    for e in area1:
        q.append(e)
        break
    while q:
        cur = q.popleft()
        if visit_area1[cur]: continue
        visit_area1[cur] = 1
        for adj in graph[cur]:
            if adj in area1:
                q.append(adj)
    if sum(visit_area1) != len(area1):
        return ret

    for e in area2:
        q.append(e)
        break
    while q:
        cur = q.popleft()
        if visit_area2[cur]: continue
        visit_area2[cur] = 1
        for adj in graph[cur]:
            if adj in area2:
                q.append(adj)
    if sum(visit_area2) != len(area2):
        return ret
    
    # 선거구가 모두 연결되어있는 경우
    ret[0] = 1
    return ret

def backtrack(ind, limit):
    global population, graph, answer, visit
    if ind == limit:
        # 선거구끼리 모두 연결되어있는지 확인
        tmp = check(limit)
        if tmp[0]:
            # 연결되어있으면 최솟값 갱신
            answer = min(answer, abs(tmp[1] - tmp[2]))
        return
    # 탐색
    visit[ind + 1] = 1
    backtrack(ind + 1, limit)
    visit[ind + 1] = 0
    backtrack(ind + 1, limit)


def main():
    global population, graph, answer, visit
    # 0 input
    N = int(input())
    population = [0] + list(map(int, input().split()))
    graph = [[] for _ in range(N + 1)]
    for i in range(1, N + 1):
        for idx, v in enumerate(map(int, input().split())):
            if idx:
                graph[i].append(v)
    # 1 find combinations
    visit = [0 for _ in range(N + 1)]
    answer = N * 100
    backtrack(0, N)
    if answer == N * 100:
        answer = -1
    # 2 output
    print(answer)


if __name__ == "__main__":
    main()

itertools.combinations를 사용하면 조금더 쉽게 조합을 만들수 있을 것 같았지만 백트래킹에 약하다보니 연습할겸 직접 구현했다.

그래프 탐색할때 중복되는 코드가 있는데 함수로 빼서 사용하면 코드가 줄어들것 같다. 

다른 사람들이랑 꽤 속도 차이가 난다.

 

'STUDY > Algorithm' 카테고리의 다른 글

[백준] 10597 순열장난 python, c/c++  (0) 2022.03.09
[백준] 1343 폴리오미노 C  (0) 2022.03.08
[백준] 15655 N과 M(6) python  (0) 2022.02.28
[백준] 11000 강의실 배정 python  (0) 2022.02.27
[백준] 14502 연구소 python  (0) 2022.02.26