본문 바로가기

카테고리 없음

그래프

그래프란

정점(vertex/node)과 간선(edge)으로 이루어진 자료구조로 원소 사이 연결관계 표현 시 유용하다.

차수(degree) : 각 정점에 대해 간선으로 이어진 정점의 수

 

그래프 표현

리스트 혹은 행렬로 표현이 가능하다

  인접 행렬 인접 리스트
공간복잡도 O(V^) O(V+E)
정점 u,v간 연결 관계 확인하는 시간 복잡도 O(1) O(min(deg(u),deg(v))
정점 v에 연결되어 있는 모든 정점 확인하는 시간 복잡도 O(V) O(deg(V))
효율적인 상황 -두 점의 연결 여부를 자주 확인하는 경우 
-간선이 정점^에 가까울 경우
- 특정 정점에 연결된 모든 정점을 자주 확인하는 경우
- 간선이 정점^보다 훨씬 작을 경우

 

BFS 로 모든 노드 방문하는 방법

1. 시작하는 정점을 큐에 넣고 방문 표시

2. 큐에서 정점을 꺼내에 그 정점과 연결된 모든 정점에 대해 3 진행

3. 처음 방문하는 정점일 경우 큐에 넣고 방문 표시

4. 큐가 빌 때까지 2 반복

-> 모든 정점이 큐에 최대 1번씩 들어가기 때문에 인접 리스트 이용 시 O(V+E), 인접 행렬 이용시 O(V^) 시간 복잡도

 

모든 간선의 길이가 같을 경우 두 지점 사이 거리 구할 때 BFS 이용 가능

 

BOJ 11724 연결 요소의 개수

import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.*

fun main() = with(BufferedReader(InputStreamReader(System.`in`))){
    var st = StringTokenizer(readLine())
    val n = st.nextToken().toInt() // 정점
    val m = st.nextToken().toInt() // 간선
    val adj = Array(n+1) { IntArray(n+1) }

    // make a graph
    repeat(m) {
        st = StringTokenizer(readLine())
        val u = st.nextToken().toInt()
        val v = st.nextToken().toInt()
        adj[u][v] = 1
        adj[v][u] = 1
    }

    // 노드 방문 탐색
    var answer = 0
    val visited =  BooleanArray(n+1) { false }
    val q = ArrayDeque<Int>()
    for (i in 1..n) {
        if (visited[i]) continue
        answer++
        q.add(i)
        visited[i] = true

        while (q.isNotEmpty()) {
            val cur = q.remove()
            for (j in 1..n) {
                if (adj[cur][j] == 0 || visited[j]) continue
                q.add(j)
                visited[j] = true
            }
        }
    }

    print("$answer")
}

 

BOJ 1260 DFS와 BFS

그래프를 dfs와 bfs로 탐색한 결과를 출력하기

import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.util.*

private var n = 0 // 정점의 수
private var m = 0 // 간선의 수
private var v = 0 // 탐색을 시작할 정점의 번호
private lateinit var adj: MutableList<MutableList<Int>>
private val bw = BufferedWriter(OutputStreamWriter(System.out))
private lateinit var visited: BooleanArray

fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
    var st = StringTokenizer(readLine())
    n = st.nextToken().toInt()
    m = st.nextToken().toInt()
    v = st.nextToken().toInt()
    adj = MutableList(n + 1) { mutableListOf() }
    visited = BooleanArray(n+1) { false }

    repeat(m) {
        st = StringTokenizer(readLine())
        val u = st.nextToken().toInt()
        val v = st.nextToken().toInt()
        adj[u].add(v)
        adj[v].add(u)
    }

    // 정렬 : 작은 번호부터 방문하기 위함
    for (i in 1..n) {
        adj[i].sort()
    }

    dfs(v)
    bw.write("\n")
    visited = BooleanArray(n+1) { false }
    bfs()
    bw.close()
}

// 재귀 dfs
private fun dfs(cur: Int) {
    visited[cur] = true
    bw.write("$cur ")

    for (next in adj[cur]) {
        if (visited[next]) continue
        dfs(next)
    }
}

private fun bfs() {
    val q = ArrayDeque<Int>()

    // bfs start from v
    q.add(v)
    visited[v] = true

    while (q.isNotEmpty()) {
        val cur = q.remove()
        bw.write("$cur ")
        for (next in adj[cur]) {
            if (visited[next]) continue

            q.add(next)
            visited[next] = true
        }
    }
}

 

BOJ 2606 바이러스

1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수 출력하기

bfs 탐색하며 1번 컴퓨터에 연결된 컴퓨터의 수를 센다

 

import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.*

fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
    val numberOfComputers = readLine().toInt()
    var edges = readLine().toInt()

    // 1. 컴퓨터 연결
    var st: StringTokenizer
    val adj = MutableList(numberOfComputers+1) { mutableListOf<Int>() }
    while (edges-- != 0) {
        st = StringTokenizer(readLine())
        val computer1 = st.nextToken().toInt()
        val computer2 = st.nextToken().toInt()

        adj[computer1].add(computer2)
        adj[computer2].add(computer1)
    }

    // 2. 바이러스 컴퓨터 숫자 세기
    var answer = 0
    val q = ArrayDeque<Int>()
    val vis = BooleanArray(numberOfComputers+1) { false }
    q.add(1)
    vis[1] = true
    while (q.isNotEmpty()) {
        val cur = q.remove()
        answer++

        for (next in adj[cur]) {
            if (vis[next]) continue
            q.add(next)
            vis[next] = true
        }
    }

    if (answer == 0) print("0")
    else print("${answer-1}")
}

 

BOJ 5567. 결혼식

상근이의 결혼식에 초대하는 동기의 수 구하기 (자신의 친구, 친구의 친구)

bfs 탐색으로 노드 간 거리 이용(노드간 거리가 2보다 크면 남이다)

import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.*

fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
    val n = readLine().toInt() // 동기 수
    var m = readLine().toInt() // 리스트 길이

    val adj = MutableList(n+1) { mutableListOf<Int>() }
    var st: StringTokenizer

    while (m-- != 0) {
        st = StringTokenizer(readLine())
        val a = st.nextToken().toInt()
        val b = st.nextToken().toInt()
        adj[a].add(b)
        adj[b].add(a)
    }

    var answer = 0
    val q = ArrayDeque<Int>()
    val vis = IntArray(n+1) { -1 }
    q.add(1)
    vis[1] = 0

    while (q.isNotEmpty()) {
        val cur = q.remove()
        answer++

        for (next in adj[cur]) {
            if (vis[next] != -1 || vis[cur] + 1 > 2) continue
            q.add(next)
            vis[next] = vis[cur] + 1
        }
    }

    if (answer == 0) print("0")
    else print("${answer-1}")
}

 

BOJ 11403 경로 찾기

가중치가 없는 방향 그래프에서 모든 정점에 대해, i->j로 가는 경로가 있는지 없는지 구하기

-> 각 노드에 대하여 bfs 탐색

(플로이드 와샬 알고리즘을 활용해도 풀 수 있음)

 

import java.io.*
import java.util.*

fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
    val n = readLine().toInt()
    val adj = Array(n) { IntArray(n) { -1 } }
    var st: StringTokenizer


    repeat(n) { i ->
        st = StringTokenizer(readLine())
        repeat(n) { j ->
            adj[i][j] = st.nextToken().toInt()
        }
    }

    for (i in 0 until n) {
        val q = ArrayDeque<Int>()
        val vis = BooleanArray(n) { false }
        q.add(i)

        // bfs
        while (q.isNotEmpty()) {
            val cur = q.remove()
            for (j in 0 until n) {
                if (adj[cur][j] == 0 || vis[j]) continue
                q.add(j)
                vis[j] = true
                adj[i][j] = 1
            }
        }
    }

    val bw = BufferedWriter(OutputStreamWriter(System.out))
    repeat(n) { i ->
        repeat(n) { j ->
            bw.write("${adj[i][j]} ")
        }
        bw.write("\n")
    }
    bw.close()
}

 

BOJ 2660 회장 뽑기

BFS 거리구하기

-> 최종 탐색 거리가 작은 사람이 회장 후보

import java.io.*
import java.util.*

fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
    val n = readLine().toInt()
    val adj = MutableList(n+1) { mutableListOf<Int>() }
    var st: StringTokenizer
    while (true) {
        st = StringTokenizer(readLine())
        val i = st.nextToken().toInt()
        if (i == -1) break
        val j = st.nextToken().toInt()
        adj[i].add(j)
        adj[j].add(i)
    }


    val people = IntArray(n+1)
    var min = 100

    for (i in 1..n) {
        val q = ArrayDeque<Int>()
        val vis = IntArray(n+1) { -1 }
        var score = 0

        q.add(i)
        vis[i] = 0

        while (q.isNotEmpty()) {
            val cur = q.remove()

            for (next in adj[cur]) {
                if (vis[next] != -1) continue
                q.add(next)
                vis[next] = vis[cur] + 1
                score = vis[next]
            }
        }

        people[i] = score
        if (min >= score)
            min = score
    }

    val candidate = mutableListOf<Int>()
    for (i in people.indices) {
        if (min == people[i])
            candidate.add(i)
    }

    val bw = BufferedWriter(OutputStreamWriter(System.out))
    bw.write("$min ${candidate.size}\n")
    repeat(candidate.size) { it ->
        bw.write("${candidate[it]} ")
    }
    bw.close()
}

 

참고

https://youtu.be/9iI6fuOLiLg