Algorithm/Baekjoon
[Swift] 17471 게리맨더링 풀이 및 코드
내일은개발천재🎵
2023. 3. 1. 23:19
문제 사이트
17471번: 게리맨더링
선거구를 [1, 4], [2, 3, 5, 6]으로 나누면 각 선거구의 인구는 9, 8이 된다. 인구 차이는 1이고, 이 값보다 더 작은 값으로 선거구를 나눌 수는 없다.
www.acmicpc.net
문제 요약
- N개의 구로 나누어진 시가 있다.
- 해당 시를 두 개의 구역(선거구)으로 나누어야한다.
- 한 선거구에 포함된 구역은 모두 연결되어야한다. (총 2개의 그래프로 구성돼야함)
- 이 때, 인구 차이의 최솟값을 구해라.
접근 방법
- 시를 2개의 구역으로 나눈다.
- 나누어진 선거구가 각각 연결되어있는지 확인한다.
- 인구수를 계산하여 최솟 값을 구한다.
접근 방법
- 시를 2개의 구역으로 나눈다.
- 비트마스킹을 사용해서 모든 경우의 수를 구했다. 이때 n/2의 경우의 수만 계산해도 된다.
- 왜냐하면 구가 [1], [2, 3, 4, 5]로 나누어진 것과 [2, 3, 4, 5], [1]로 나누어진 경우의 수는 동일하기 때문이다.
- 나누어진 선거구가 연결되어다면 최솟 값을 구한다.
- BFS로 탐색한다.
풀이
- 그래프 입력 방법
- input을 통해 값을 받고 맨 처음 있는 정보는 지운다. (연결된 노드의 개수는 필요 없는 정보)
- 이후 입력받은 정보에서 -1을 하는 이유는 비트마스킹으로 받은 경우의 수가 0부터 이루어져있기 때문이다. (예시는 1번부터 n번까지로 주어지기 때문에 정보를 통일시켜야한다.)
- BFS를 다음과 같이 풀었다.
- 매개변수에 시작노드, 해당 노드가 포함된 선거구를 매개변수로 받는다.
- 현재 노드와 연결된 노드들 중 방문하지 않았고, 같은 선거구에 있는 경우 방문처리를 한다.
- 구가 [1, 2, 3] [4, 5, 6] 으로 나누어져있다고 가정하면node 4와 [4, 5, 6]을 탐색한다.
- BFS가 모두 종료된 후 모든 노드가 방문처리 되었다면, 구는 정확히 두 개로 나누어져있다고 볼 수 있다.
- node 1과 [1, 2, 3]
- 결과 출력하는 방법
- result의 초깃값은 Int.max로 둔다.
- BFS를 통해 구가 정확히 두 개로 나누어졌다면 기존 result와 새로 계산한 result 중 최솟값으로 갱신한다.
- 최종 result가 Int.max와 동일하다면 -1, 동일하지 않다면 result를 출력한다.
- → 인구수의 최댓값이 100이므로 가능하다~!
코드
let n = Int(readLine()!)! // 구역의 개수
let peoples = readLine()!.split { $0 == " " }.map { Int($0)! } // 구역의 인구 수
var map = [[Int]](repeating: [], count: n)
var visited = [Bool](repeating: false, count: n)
var result = Int.max
// 그래프 입력 방법
for i in 0 ..< n {
var input = readLine()!.split { $0 == " " }.map { Int($0)! - 1 }
input.removeFirst()
map[i] = input
}
var divide = divided()
for (a, b) in divide {
visited = [Bool](repeating: false, count: n)
let p1 = bfs(n: a[0], area: a)
let p2 = bfs(n: b[0], area: b)
if visited.filter({ !$0 }).isEmpty {
result = min(result, abs(p1 - p2))
}
}
print(result == Int.max ? -1 : result)
// 비트마스킹을 통한 경우의 수 구하기
func divided() -> [([Int], [Int])] {
var result = [([Int], [Int])]()
for i in 0 ..< (1 << n/2) { // n의 제곱, 중복 제거
var area1 = [Int]()
var area2 = [Int]()
for j in 0 ..< n { // n
if i & (1 << j) != 0 {
area1.append(j)
} else {
area2.append(j)
}
}
guard !area1.isEmpty && area1.count != n else { continue }
result.append( (area1, area2))
}
return result
}
// bfs
func bfs(n: Int, area: [Int]) -> Int {
var person = peoples[n]
visited[n] = true
var queue = [n]
while !queue.isEmpty {
let here = queue.removeFirst()
for next in map[here] {
if !visited[next] && area.contains(next) {
visited[next] = true
queue.append(next)
person += peoples[next]
}
}
}
return person
}