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



모든 점을 방문할 수 있는지 확인하면 되는 문제이므로 bfs / dfs로 기본적으로 접근할 수 있다.


또한 n이 작기 때문에 플로이드 와샬도 가능하다. 가중치는 모두 1로 두면 되겠다.




이러한 문제를 풀 때 신경써줄 부분은, 자기 자신을 곧바로 방문할 수 있는지 확인하는 것이다.


이 문제에서는 노드1->노드1 이런식으로 곧바로 자기 자신을 방문할 수 없고, 1->2->3->1 이런식으로 사이클을 거쳐서 root(자기 자신)을 방문할 수 있다.


따라서 플로이드 와샬 알고리즘을 적용할 때, 비용을 무한대로 초기화 하는 과정에서, 자기 자신으로 가는 비용도 무한대로 초기화해주고 시작해야 한다.




비슷한 맥락으로, DFS로 문제를 풀이할 때, 보통 시작점을 방문처리하고 알고리즘을 시작한다.


하지만 시작점을 방문처리하고, 미방문한 지점을 이어서 방문하도록 알고리즘을 구현하면


1-> 2-> 3-> 1  이 과정에서, 마지막에 3->1을 방문할 수가 없다. 초기에 시작 지점인 1을 방문처리 했기 때문이다.


따라서 이것을 방지하기 위해서 시작지점을 방문처리하지 않는다. 3->1을 거쳐서 시작 지점에서 다시 for 문을 돌면 어짜피 갈 수 있는 곳이 없으므로 재귀를 탈출하게 된다.




1. DFS


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
#pragma warning(disable :4996)
#include<iostream>
using namespace std;
 
int m[101][101];
bool vis[101][101], chk[11];
int n, root;
void dfs(int cur) {
 
    for (int i = 0; i < n; i++) {
        if (m[cur][i] == 1 && !chk[i]) {
            chk[i] = true;
            dfs(i);
        }
    }
}
 
int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n;
 
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            cin >> m[i][j];
    //cout << '\n';
 
    for (int i = 0; i < n; i++) {
        dfs(i); // i->i로 곧바로 갈 수 없으므로 시작점 방문처리 안 함
 
        //노드i에서 시작해서 방문할 수 있는 점들 표시
        for (int j = 0; j < n; j++) {
            if (chk[j]) cout << 1 << ' ';
            else cout << 0 << ' ';
        }
        cout << '\n';
 
        //초기화
        for (int i = 0; i < n; i++
            chk[i] = false;
        
    }
 
    return 0;
}
 
cs




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
#include<iostream>
#include<algorithm>
#define INF 100000
using namespace std;
 
int n; //정점 개수
int m[101][101];
 
void FW() { //플로이드 와샬
    for (int k = 0; k < n; k++)
        for (int j = 0; j < n; j++)
            for (int i = 0; i < n; i++)
                m[i][j] = min(m[i][j], m[i][k] + m[k][j]);
}
int main(void) {
 
    cin >> n;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
        
            cin >> m[i][j];
            if (m[i][j] == 0 ) m[i][j] = INF;
            //자기 자신으로 갈 수 없다고 봄 -> 자기 자신으로 가는 비용도 무한초기화
        }
    
    FW();
 
    //cout << '\n';
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (m[i][j] == INF) //비용이 무한 -> 방문할 수 없다는 의미
                cout << 0 << ' ';
            else
                cout << 1 << ' ';
        }
        cout << '\n';
    }
    
    
    return 0;
}
cs


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


모든 노드의 연결 관계를 파악해야한다.


n 이 500이므로 플로이드 와샬을 사용할 수 있고, 모든 정점으로부터 다른 모든 정점까지의 비용을 파악할 수 있다.


비용은 1로 두고, 갈 수 있는지 없는지만 파악하면 된다.


주의할 사항은, degree를 계산할 때, 자신에서 자신으로 가는 것은 카운트하지 않아야 한다는 것이다.


자기 자신으로의 경로 비용 초기화를 어떻게 했는지에 따라 잘 처리해주면 되겠다.



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
#include<iostream>
#include<algorithm>
#define INF 100000000
using namespace std;
int map[501][501], m, n, degree[501];
void FW() {
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                map[i][j] = min(map[i][j], map[i][k] + map[k][j]);
            }
        }
    }
 
    // 노드 i까지 갈 수 있는 노드의 수 + 노드 i에서 갈 수 있는 노드수
    // 이게 n-1이면 됨. degree 계산
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (map[i][j] != INF && i != j) {
                degree[i]++;
                degree[j]++;
            }
        }
    }
}
int main(void) {
    cin >> n >>m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            map[i][j] = i==j ? 0 : INF;
        }
 
    for (int i = 0; i < m; i++) {
        int src, dst;
        cin >> src >> dst;
        map[src][dst] = 1;
    }
    FW();
    int res = 0;
    for (int i = 1; i <= n; i++)
        if (degree[i] == n - 1) res++;
    cout << res;
    return 0;
}
 
//n이 500 -> 행렬에 다 넣어도 됨
//비용 1로두고 플와 돌려서 모든 간선 연결 및 비용 파악
 
cs


'알고리즘 문제 풀이 > 백준 온라인 저지' 카테고리의 다른 글

백준 1926번 그림 (C++)  (0) 2019.08.19
백준 10254 고속도로 C++  (0) 2019.08.17
백준 1806 부분합 C++  (0) 2019.08.16
백준 11404 플로이드 C++  (0) 2019.08.16
백준 11657 타임머신 C++  (0) 2019.08.16

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


상관없는지 모르겠으나 원래는 kij순으로 루프 돌아야한다


문제 이름에서부터 플로이드 와샬 알고리즘을 사용해야 한다는 것을 암시하고 있는 문제이다.


주의할 것은, 이전까지 INF 값으로 limits.h 헤더에 있는 INT_MAX를 사용했는데, 이 값에 어떤 값이 더해지는 연산을 하게되면 오버플로우가 발생한다.


다익스트라, 벨만포드에서는 괜찮았지만 플로이드에서 이런 일이 생기니, 그냥 속편하게 내가 INF를 지정해서 써야겠다.


INF는 항상 (간선 가중치의 최댓값 ) * (정점 개수 - 1 ) 보다 크게 잡아줘야 한다.


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
#include<iostream>
#include<algorithm>
#include<limits.h>
using namespace std;
int n, m;
int dis[101][101];
const int MAX = 1000000000//INF는 항상 (간선 가중치의 최댓값) * (정점 개수 - 1) 보다 큰 값
void FW() {
    for (int k = 1; k <= n; k++
        for (int j = 1; j <= n; j++
            for(int i = 1; i <= n; i++
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
 
int main(void) {
    //ios::sync_with_stdio(false);
    cin.tie(0);
        
    cin >> n >> m;
    for (int i = 1; i <= n; i++
        for (int j = 1; j <= n; j++
            dis[i][j] = i == j ? 0 : MAX;
        
    for (int i = 0; i < m; i++) {
        int src, dst, cost;
        cin >> src >> dst >> cost;
        dis[src][dst] = min(dis[src][dst], cost);
    }
 
    FW();
 
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (dis[i][j] == MAX) dis[i][j] = 0;
             cout << dis[i][j] << ' ';
        }
        cout << '\n';
    }
 
    return 0;
}
 
 
cs


+ Recent posts