Uknow's Lab.
article thumbnail

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

 

10216번: Count Circle Groups

백준이는 국방의 의무를 수행하기 위해 떠났다. 혹독한 훈련을 무사히 마치고 나서, 정말 잘 생겼고 코딩도 잘하는 백준은 그 특기를 살려 적군의 진영을 수학적으로 분석하는 일을 맡게 되었

www.acmicpc.net

 

난이도 : 골드 4
태그 : 자료 구조, 그래프 이론, 그래프 탐색, 기하학, 분리 집합

 

 

설명

통신 범위가 직/간접적으로 겹치는 부대는 하나의 부대처럼 행동합니다.

즉, 통신범위가 겹치는 그룹을 모두 같은 그룹으로 묶어 몇 개의 그룹이 나오는지 구하는 문제입니다.

 

그룹화와 서로 다른 그룹의 개수를 구한다는 점에서

분리집합과 유니온 파인드를 사용할 수 있을 것 같습니다.

 

분리 집합은 서로소 집합이라고도 합니다.

서로 겹치지 않는 원소를 가지고 있는 집합이지요.

자세한 내용은 아래 포스팅을 참고해주세요.

https://uknowblog.tistory.com/61

 

[자료구조] 분리 집합(Disjoint set)과 유니온 - 파인드(Union-Find)

분리집합? 분리집합이란 서로소 집합이라고도 불립니다. 이름에서도 알 수 있듯, 각각의 집합이 공통 원소를 가지지 않는 집합입니다. 즉 전체 집합 U에 두 개의 집합 A, B가 있을 때 A ∩ B = Ø 가

uknowblog.tistory.com

 

 

유니온 - 파인드 알고리즘을 사용하여

통신 범위가 겹치는 두 그룹끼리 서로 같은 그룹으로 묶어줌으로써

최종적으로 우리는 서로 다른 그룹을 알아낼 수 있습니다.

 

그렇다면, 서로간의 통신이 겹치는지는 어떻게 확인할 수 있을까요?

통신 범위는 특정 좌표 (x, y)를 기준으로 반지름 r 만큼의 원의 범위입니다.

 

 

d = root ( (x1 - x2) ^ 2 + (y1 - y2) ^ 2) )

두 원 간의 중심의 거리(d)는 피타고라스로 쉽게 구할 수 있습니다.

두 원의 중심의 거리 d가 r1 + r2 보다 클 경우, 두 그룹은 서로 통신할 수 없습니다.

 

 

 

 

반면의 두 원의 중심 사이의 거리(d)가 두 원의 반지름의 합(r1+r2) 보다 작거나 같다면

두 원은 서로 접하거나 겹치는 부분이 생기며 한 그룹으로 합쳐집니다.

즉, d <= r1 + r2 일 때, 두 그룹은 서로 같은 그룹이 됩니다.

 

 

 

소스코드

package baekjoon.gold.g4.`Count Circle Groups`

import java.util.StringTokenizer
import kotlin.math.pow
import kotlin.math.sqrt

data class Circle(val x: Int, val y: Int, val r: Int)

lateinit var parent: IntArray

fun main() = with(System.`in`.bufferedReader()) {
    val sb = StringBuilder()
    
    repeat(readLine().toInt()) {
        val n = readLine().toInt()
        val circles = Array(n) {
            val st = StringTokenizer(readLine())
            Circle(st.nextToken().toInt(), st.nextToken().toInt(), st.nextToken().toInt())
        }

        parent = IntArray(n) { it }

        for (i in 0..<n) {
            for (j in i + 1..<n) {
                if (isConnected(circles[i], circles[j])) union(i, j)
            }
        }

        val parentSet = HashSet<Int>()
        for (i in 0..<n) {
            parentSet.add(find(i))
        }

        sb.appendLine(parentSet.size)
    }

    print(sb)
}

fun isConnected(circle1: Circle, circle2: Circle): Boolean {
    val (x1, y1, r1) = circle1
    val (x2, y2, r2) = circle2

    val d = sqrt((x1 - x2).toDouble().pow(2.0) + (y1 - y2).toDouble().pow(2.0))

    return d <= r1 + r2
}


fun find(x: Int): Int {
    if (parent[x] == x) return x
    parent[x] = find(parent[x])
    return parent[x]
}

fun union(x: Int, y: Int) {
    val (px, py) = find(x) to find(y)
    parent[py] = px
}

 

여기서 주의할 부분은 서로 다른 그룹의 개수를 카운트하는 부분입니다.

각 통신탑의 최상위 노드(루트) 중,  서로 다른 루트의 개수를 구하는 것인데요.

저는 중복제거를 위해 Set 자료구조를 사용하였습니다.

 

 

 

후기

기하학 + 분리집합이라는 다소 독특한 문제였습니다.

비슷한 문제가 있다면 또 풀어보고 싶네요.

profile

Uknow's Lab.

@유노 Uknow

인생은 Byte와 Double 사이 Char다. 아무말이나 해봤습니다.