[Python] 2150 - Strongly Connected Component (플래티넘5)

2022. 6. 29. 19:14·Coding Test/Solution

1. 문제 설명

 

2150번: Strongly Connected Component

첫째 줄에 두 정수 V(1 ≤ V ≤ 10,000), E(1 ≤ E ≤ 100,000)가 주어진다. 이는 그래프가 V개의 정점과 E개의 간선으로 이루어져 있다는 의미이다. 다음 E개의 줄에는 간선에 대한 정보를 나타내는 두 정

www.acmicpc.net

되게 힘든 싸움이었는데, 재밌는 문제였다.

 


2. 아이디어

이번 문제는 SCC를 구현해야 하는데 그래프 상에 사이클로 돌아가는 부분을 찾아내서 묶어야 한다.

그래프가 싸이클인지 아닌지 어떻게 알 수 있을까?

단순히 1에서 출발해서 다시 1로 돌아오는지 확인하는 방법으로는 엉뚱한 범위의 SCC가 형성될 수도 있다.

예를 들어, 1 -> 2 -> 3 까지 갔다가 3에서 1과 4로 간선이 연결되어 있다면...1로 돌아오긴 했는데

자칫하다간 4까지 SCC로 엮여버릴 수 있다.

이걸 해결하기 위한 아이디어 자체는 떠올리기 쉽다. 정방향으로 간 순서로 역방향으로 돌아올 수 있다면,

그 구역은 사이클이라고 볼 수 있다.

과연 그런게 가능할까? 가능하다.

 

역순으로 돌리기 위해서는 2가지 정보가 필요한데 처음 입력 받은 정점 정보를 뒤집은 그래프와

실제로 정방향으로 그래프 순회를 했을 때에 조회한 정점의 순서를 역순으로 정렬한 리스트이다.

(이게 왜 필요한지는 뒤에서 설명.)

 

첫 번째는 입력받을 때 같이 만들어버려도 되고, 나중에 기존의 정보 순서를 뒤집어 버려도 된다.

2번같은 경우에는 굉장히 간단한 방법이 있다. DFS를 돌려버리면 된다.

깊이 우선으로 그래프를 순회하다가 재귀를 빠져나오는 과정에서 정점 위치를 queue에 push한다.

다만, 한 번 방문한 정점에 대해서는 방문처리를 함으로써 무한 반복에 빠지지 않도록 유의한다.

이 과정을 1에서 N+1까지 반복해야 하는데 간선이 연결 안 된 정점이 있을 수 있기 때문에 꼭 다 확인해야 한다!

 

지금까지 얻어낸 정보

아니 왜 stack이라고 써놨지..? FIFO 이므로 스택보단 큐가 맞다.

그렇다고 정말 큐를 import해야 하고 그럴 필요는 없다. 그냥 리스트를 큐처럼 쓰는 것 뿐.

어쨌든 이제 값을 차례로 넣어보자.

 

한 바퀴 돌리면 5 4 1이 방문처리 되면서 SSC에 묶이게 된다.

아직 5만 넣고 돌렸으므로 큐의 4, 1이 실제로 지워지진 않았지만 의미없는 데이터가 된다.

 

두 번째 돌렸을 때의 모습.

SSC가 만들어질 때마다 sort()해서 어디 잘 보관해두면 끝난다.

다만 의문인 점이 왜 굳이 1부터 안 돌리고 큐를 만들어서 돌리는 등의 귀찮은 과정을 수반하느냐?

 

바로 여기서 6과 같은 존재가 문제를 일으킬 수 있다.

1과 2를 순서대로 넣으면 SSC가 아래처럼 묶인다.

 

이런 상황을 방지해주기 위해서 일련의 '데이터 흐름'을 만들어 주어 6처럼 홀로 동떨어진 정점을

자연스레 걸러낼 수가 있게 된다.

 

솔직히 당시에는 휴먼 컴파일로만 판단하고 이게 될까 싶었는데 진짜 되서 나도 놀랐었다.

 


3. 코드

import sys

sys.setrecursionlimit(10 ** 8)
input = sys.stdin.readline

def reverse_graph(v, graph):
    rev_graph = [[] for _ in range(v+1)]

    for i in range(1, v+1):
        for j in graph[i]:
            rev_graph[j].append(i)
    return rev_graph

def dfs(node, graph, visited, queue:list):
    visited[node] = 1

    for next_node in graph[node]:
        if visited[next_node] == 0:
            #queue.append(next_node)
            dfs(next_node, graph, visited, queue)
    queue.append(node)

def make_queue(v, graph, visited):
    queue = []

    for node in range(1, v+1):
        if visited[node] == 0:
            dfs(node, graph, visited, queue)
    return queue

def reverse_dfs(node, graph, visited, scc:list):
    visited[node] = 1
    scc.append(node)

    for next_node in graph[node]:
        if visited[next_node] == 0:
            reverse_dfs(next_node, graph, visited, scc)

def check_ssc(queue:list, graph, visited):
    cnt, result = 0, []

    while queue:
        scc = []
        node = queue.pop()
        if visited[node] == 0:
            reverse_dfs(node, graph, visited, scc)
            result.append(sorted(scc))
            cnt += 1
    result.sort()
    return cnt, result

def solution():
    v, e = map(int, input().split())
    graph = [[] for _ in range(v+1)]
    visited = [0] * (v+1)

    for _ in range(e):
        a, b = map(int, input().split())
        graph[a].append(b)

    queue = make_queue(v, graph, visited)
    graph = reverse_graph(v, graph)
    visited = [0] * (v+1)
    cnt, result = check_ssc(queue, graph, visited)

    print(cnt)
    for i in result:
        for j in i:
            print(j, end=" ")
        print(-1)

solution()
저작자표시 비영리 (새창열림)
'Coding Test/Solution' 카테고리의 다른 글
  • [Python] 2749 - 피보나치 수 3 (골드2)
  • [Python] 4196 - 도미노 (플래티넘4)
  • [Python] 2623 - 음악 프로그램 (골드3)
  • [Python] 1516 - 게임개발 (골드3)
나죽못고나강뿐
나죽못고나강뿐
싱클레어, 대부분의 사람들이 가는 길은 쉽고, 우리가 가는 길은 어려워요. 우리 함께 이 길을 가봅시다.
코드를 찢다싱클레어, 대부분의 사람들이 가는 길은 쉽고, 우리가 가는 길은 어려워요. 우리 함께 이 길을 가봅시다.
  • 나죽못고나강뿐
    코드를 찢다
    나죽못고나강뿐
  • 전체
    오늘
    어제
    • 분류 전체보기 (473)
      • Computer Science (60)
        • Git & Github (4)
        • Network (17)
        • Computer Structure & OS (13)
        • Software Engineering (5)
        • Database (9)
        • Security (5)
        • Concept (7)
      • Frontend (22)
        • React (14)
        • Android (4)
        • iOS (4)
      • Backend (81)
        • Spring Boot & JPA (52)
        • Django REST Framework (14)
        • MySQL (10)
        • Nginx (1)
        • FastAPI (4)
      • DevOps (24)
        • Docker & Kubernetes (11)
        • Naver Cloud Platform (1)
        • AWS (2)
        • Linux (6)
        • Jenkins (0)
        • GoCD (3)
      • Coding Test (112)
        • Solution (104)
        • Algorithm (7)
        • Data structure (0)
      • Reference (138)
        • Effective-Java (90)
        • Pragmatic Programmer (0)
        • CleanCode (11)
        • Clean Architecture (4)
        • Test-Driven Development (4)
        • Relational Data Modeling No.. (0)
        • Microservice Architecture (2)
        • 알고리즘 문제 해결 전략 (9)
        • Modern Java in Action (0)
        • Spring in Action (0)
        • DDD start (0)
        • Design Pattern (6)
        • 대규모 시스템 설계 (7)
        • JVM 밑바닥까지 파헤치기 (4)
        • The Pragmatic Programmer (1)
      • Service Planning (2)
      • Side Project (5)
      • AI (0)
      • MATLAB & Math Concept & Pro.. (2)
      • Review (22)
      • Interview (3)
      • IT News (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃
  • 공지사항

    • 한동안 포스팅은 어려울 것 같습니다. 🥲
    • N Tech Service 풀스택 신입 개발자가 되었습니다⋯
    • 취업 전 계획 재조정
    • 취업 전까지 공부 계획
    • 앞으로의 일정에 대하여..
  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
나죽못고나강뿐
[Python] 2150 - Strongly Connected Component (플래티넘5)

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.