Henu
개발냥발
Henu
전체 방문자
오늘
어제
  • 분류 전체보기 (411)
    • DevOps (52)
      • Kubernetes (19)
      • Docker (14)
      • AWS (3)
      • Nginx (4)
      • Linux (4)
      • ArgoCD (1)
      • CN (2)
      • NATS (0)
      • Git (5)
    • Back-End (30)
      • Django (18)
      • Spring (5)
      • JPA (1)
      • MSA (5)
    • CS (87)
      • SystemSoftware (20)
      • OS (25)
      • Computer Architecture (16)
      • Network (23)
      • Database (2)
    • Lang (21)
      • Java (9)
      • Python (4)
      • C# (8)
    • Life (12)
    • 블록체인 (2)
    • Algorithm (204)
      • BOJ (160)
      • 프로그래머스 (19)
      • LeetCode (4)
      • SWEA (1)
      • 알고리즘 문제 해결 전략 (8)
      • DS, algorithms (7)
      • Checkio (5)
    • IT (2)

블로그 메뉴

  • GitHub
  • 글쓰기
  • 관리자

공지사항

  • Free!

인기 글

태그

  • 백트래킹
  • django
  • Kubernetes
  • 프로그래머스
  • BFS
  • boj
  • Network
  • 다이나믹 프로그래밍
  • docker
  • DFS

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Henu

개발냥발

[백준 4196] 도미노 C++
Algorithm/BOJ

[백준 4196] 도미노 C++

2021. 7. 1. 13:58
 

4196번: 도미노

도미노는 재밌다. 도미노 블록을 일렬로 길게 늘어세운 뒤 블록 하나를 넘어뜨리면 그 블록이 넘어지며 다음 블록을 넘어뜨리는 일이 반복되어 일렬로 늘어선 블록들을 연쇄적으로 모두 쓰러

www.acmicpc.net

강한 연결 요소


1.  문제 해결 아이디어

 

 

강한 연결 요소 (SCC) - 타잔 알고리즘

[백준 2150] Strongly Connected Component C++ 2150번: Strongly Connected Component 첫째 줄에 두 정수 V(1 ≤ V ≤ 10,000), E(1 ≤ E ≤ 100,000)가 주어진다. 이는 그래프가 V개의 정점과 E개의 간선으로 이..

hyeo-noo.tistory.com

타잔 알고리즘을 사용해서 풀었다.

개인적으로 코사라주보다 직관적이고 이해하기 편해서 SCC문제를 풀 때는 주로 타잔 알고리즘을 사용할 것 같다.

 

 

하나의 도미노가 무너지면 다른 도미노에 영향을 끼친다.

임의의 도미노에서 출발해서, 이어질 수 있는 모든 도미노는 무너질 것이다!

 

특정 도미노들이 사이클을 이룬다고 하면 사이클을 이루는 도미노들 중에서 하나만 넘어져도 사이클에 속한 모든 도미노는 무너질 것이다!

이 사실로 보아 SCC문제라고 생각할 수 있다.

 

하지만 도미노의 답은 단순한 SCC는 아니다.

 

1 -> 2

2 -> 3

3 -> 1

4 -> 3

5 -> 4

위와 같은 연결이 형성되어있을 때 SCC는 (1,2,3), (4), (5) 이렇게 3개이지만

5번 도미노를 처음에 무너뜨리면 모든 도미노를 무너뜨릴 수 있어서 한 번의 터치가 답이된다.

 

그러면 답을 어떻게 구하냐

 

각 정점은 사이클에 속하든 속하지 않든 dfs방문 순서와 SCC생성 순서에 따라서 반드시 어떤 SCC에 속하게 된다.

 

위 예시에서 (1,2,3)을 1번 SCC, (4)를 2번 SCC, (5)를 3번 SCC라고 하자.

SCC간의 연결을 고려했을 때 SCC(3) -> SCC(2) -> SCC(1) 이렇게 연결되어있다.

 

처음에 5번 도미노를 무너뜨리면 모든 도미노를 무너뜨릴 수 있었다.

5번 도미노는 3번 SCC이다.

3번 SCC의 특징! => SCC의 indegree가 0이다!

 

곰곰히 생각해보자

어떤 도미노가 SCC를 건드리면 SCC내부는 모두 무너질 것이고 하나의 도미노로 취급해도 될 것이다.

그럼 건드릴 수 없는(indegree가 0인)SCC는 어떻게 무너질까?

 - 손으로 넘어뜨려야지

그래서 indegree가 0인 SCC의 개수가 답이될것이다.!

 

그럼 SCC의 indegree를 각각 구하면 되겠다

 

모든 정점의 연결을 살펴보면서 두 정점의 SCC가 같다면 연결을 당하는? SCC의 indegree를 1 증가시켜준다.

 

그리고 모든 SCC의 indegree를 검사하면서 indegree값이 0인 SCC의 개수를 출력하면 끝이다.


2.  코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>
#include <vector>
#include <cstring>
 
using namespace std;
 
int N, M;
vector< vector<int> > adj;
vector<int> stk;
 
int sccindegree[100001];
int SCCID[100001];
int Vertexcnt, SCCcnt;
int discovered[100001];
 
int SCC(int now){
    int ret = discovered[now] = Vertexcnt++;
    
    stk.push_back(now);
    
    for(auto &next : adj[now]){
        if(discovered[next] == -1){
            ret = min(ret, SCC(next));
        }
        else if(SCCID[next] == -1){
            ret = min(ret, discovered[next]);
        }
    }
    
    if(ret == discovered[now]){
        while(1){
            int temp = stk.back();
            stk.pop_back();
            SCCID[temp] = SCCcnt;
            if(temp == now) break;
        }
        SCCcnt++;
    }
    
    return ret;
}
 
void init(){
    memset(discovered, -1, sizeof(discovered));
    memset(SCCID, -1, sizeof(SCCID));
    memset(sccindegree, 0, sizeof(sccindegree));
    adj.clear();
    adj.resize(N+1, vector<int>());
    Vertexcnt = SCCcnt = 0;
    
    int u, v;
    for(int i = 0; i < M; i++){
        cin >> u >> v;
        adj[u].push_back(v);
    }
}
 
void solve(){
    int result = 0;
    
    for(int i = 1; i <= N; i++){
        if(discovered[i] != -1) continue;
        SCC(i);
    }
    
    for(int i = 1; i <= N; i++){
        for(auto &w : adj[i]){
            if(SCCID[w] == SCCID[i]) continue;
            sccindegree[SCCID[w]]++;
        }
    }
    
    for(int i = 0; i < SCCcnt; i++){
        if(!sccindegree[i]) result++;
    }
    
    cout << result << "\n";
}
 
int main(){
    // cout.tie(0)를 넣어주면 런타임에러가 발생한다 왜??
    ios::sync_with_stdio(0), cin.tie(0);
    int test;
    cin >> test;
    while(test--){
        cin >> N >> M;
        
        init();
        solve();
        
    }
    
    return 0;
}
Colored by Color Scripter
cs

 

의식의 흐름대로 풀이를 써봤다

개인적으로 이게 더 편하다

글을 쓸 때 압축해서 간결하게 쓰고싶은 마음이 컸는데 피곤해서 대충..쓰는 느낌으로 쓰니까 더 편하고 잘 써지는것 같다

 

 

 

글쓰는 스타일, 문제 풀이 해석 등에 대한 피드백 감사히 받겠습니다.

'Algorithm > BOJ' 카테고리의 다른 글

[백준 3197] 백조의 호수  (0) 2021.07.01
[백준 2636] 치즈 C++  (0) 2021.07.01
[백준 1562] 계단 수 C++  (0) 2021.06.29
[백준 2623] 음악프로그램 C++  (0) 2021.06.29
[백준 11266] 단절점 C++  (0) 2021.06.27
    'Algorithm/BOJ' 카테고리의 다른 글
    • [백준 3197] 백조의 호수
    • [백준 2636] 치즈 C++
    • [백준 1562] 계단 수 C++
    • [백준 2623] 음악프로그램 C++

    티스토리툴바