문제


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


풀이


flow network 구상부터 parametric search를 사용하는 아이디어까지, 다이아 4에 걸맞는 문제였다.

적국과 인접한 지역들에 있는 병력의 최솟값의 최댓값을 구하는 문제다.

우선 source와 1~n번 노드들을 초기 군대 수를 capacity로 연결시켜준다.

그리고 한 턴 뒤의 1~n번 노드들을 sink로 연결시켜주는데, 적국과 인접하지 않은 지역은 capacity 1로 연결해도 충분하다.

문제는 적국과 인접한 지역들이다.

이렇게 ‘최솟값의 최댓값’ 등을 구하는 문제엔, 정답을 고정시키고 참 거짓 여부를 가리며 범위를 좁혀나가는 매개 변수 탐색법이 효과적이다.

그러므로 적국과 인접한 지역들은 상수 mid로 capacity를 통일한 후 sink에 이어준다.

나머지 노드들 간의 엣지는, 인접한 두 아군 지역에 대해 capacity 무한대로 연결시켜주면 된다.

물론 병력을 이동시키지 않을 수도 있기 때문에, 같은 번호의 한 턴 뒤 노드에도 capacity 무한대로 연결시켜준다.

이렇게 구성한 flow network에서 최대 유량을 구한 후, sink로 흘러들어가는 간선들의 flow가 모두 포화 상태라면 이 mid 값은 유효한 것이다.


코드


import sys
from copy import deepcopy
from collections import deque

for _ in range(int(sys.stdin.readline())):
    n = int(sys.stdin.readline())
    army = list(map(int, sys.stdin.readline().split()))
    board = [sys.stdin.readline().strip() for __ in range(n)]

    graph_ = [[0] * (2 * n + 2) for __ in range(2 * n + 2)]

    for i in range(n):
        if army[i]:
            graph_[0][i + 1] = army[i]
            graph_[i + 1][i + n + 1] = float('inf')
            graph_[i + n + 1][-1] = 1
            for j in range(i + 1, n):
                if board[i][j] == 'Y':
                    if army[j]:
                        graph_[i + 1][j + n + 1] = float('inf')
                        graph_[j + 1][i + n + 1] = float('inf')

    left = 1
    right = 9802
    while left <= right:
        mid = (left + right) // 2

        graph = deepcopy(graph_)

        for i in range(n):
            if not army[i]:
                for j in range(n):
                    if (board[i][j] == 'Y') and army[j]:
                        graph[j + n + 1][-1] = mid

        total = 0
        while True:
            prev = [-1] * (2 * n + 2)
            queue = deque([(0, float('inf'))])
            while queue:
                cur, res = queue.popleft()

                for nxt in range(1, 2 * n + 2):
                    if (nxt == 2 * n + 1) and graph[cur][-1]:
                        res = min(res, graph[cur][-1])
                        prev[-1] = cur
                        break

                    if (prev[nxt] == -1) and graph[cur][nxt]:
                        prev[nxt] = cur
                        queue.append((nxt, min(res, graph[cur][nxt])))

                if prev[-1] != -1:
                    break
            
            if prev[-1] == -1:
                break

            total += res
            while nxt:
                cur = prev[nxt]
                graph[cur][nxt] -= res
                graph[nxt][cur] += res
                nxt = cur
        
        flag = 0
        for i in range(n):
            if army[i]:
                flag += graph[i + n + 1][-1]
        
        if flag:
            right = mid - 1
        else:
            left = mid + 1
    
    print(right)

Leave a comment