기술 블로그

17472번 다리 만들기 2 본문

알고리즘 문제/BOJ

17472번 다리 만들기 2

parkit 2019. 9. 27. 00:50
728x90
반응형

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



1. DFS로 각 섬을 Group화 한다.(1, 2, 3, ...)


2. BFS로 각 정점끼리의 거리를 구하는데 이 때, 4가지 방향(동서남북)에 대한 배열(코드의 vertex)을

활용해야 한다. 이유는 아래 예제를 생각해보자.

(주의 : 나의 코드에서는 방문 처리를 말하는 것이 아니다. 

다른 방법으로 방문 처리를 4가지 방향으로 해도 된다. 단순 이 글에서의 설명이다.)


1
2
3
4
5
6
5 10
1 1 1 1 1 1 1 1 1 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 1 0 0 1
1 0 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1 1 1 
cs



3. MST로 활용하기 위해, mst vector에 {간선의 가중치, 현재 정점, 다음 정점}을 담는다.


4. 간선의 가중치를 기준으로 오름차순한다.


5. 사이클 방지를 위해 Union-Find를 활용한다.


6. MST 특성상 최소 다리 개수는 전체 정점 개수 - 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
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <bits/stdc++.h>
 
using namespace std;
 
#define Max 11
#define INF 987654321
 
// bfs를 위한 구조체
typedef struct info
{
    int y, x, d;
}info;
 
// mst를 위한 구조체
typedef struct info2
{
    int w, here, next;
}info2;
 
int m[Max][Max], N, M, answer, gc = 1, parent[Max], vertex[Max][4];
int dy[4= { 010-1 };
int dx[4= { 10-10 };
vector<pair<intint> > v[Max];
vector<info2> mst;
bool visit[Max][Max];
 
bool cmp(const info2 & a, const info2 & b)
{
    if (a.w < b.w) return true;
    return false;
}
 
// 그룹화
void dfs(int r, int c, int group)
{
    visit[r][c] = true;
    m[r][c] = group;
    for (int i = 0; i < 4; i++)
    {
        int y = r + dy[i];
        int x = c + dx[i];
        if (y < 0 || y >= N || x < 0 || x >= M || visit[y][x] || !m[y][x]) continue;
        dfs(y, x, group);
    }
}
 
// 각 정점끼리의 거리 구하기. 이 때, vertex 배열을 유심히 보자.
int bfs(int sy, int sx, int from, int to)
{
    memset(visit, falsesizeof(visit));
    memset(vertex, 0sizeof(vertex));
    visit[sy][sx] = true;
    
    queue<info> q;
    for (int i = 0; i < 4; i++) q.push({ sy, sx, i }); // 시작부터 4가지 방향을 고려해야한다.
 
    int ret = -1;
 
    while (!q.empty())
    {
        int qs = q.size();
 
        while (qs--)
        {
            int r = q.front().y;
            int c = q.front().x;
            int d = q.front().d;
 
            q.pop();
 
            // to 정점에 도착했고, 아직 그 to정점에 대한 방향(d)의 값이 설정되지 않았다면(도착하지 않았다면)
            if (m[r][c] == to) if(!vertex[to][d]) vertex[to][d] = ret;
                        
            int y = r + dy[d];
            int x = c + dx[d];
 
            if (y < 0 || y >= N || x < 0 || x >= M || visit[y][x]) continue;
 
            if (m[y][x] == 0 || m[y][x] == to) // 실수 주의
            {
                visit[y][x] = true;
                q.push({ y, x, d });
            }
        }
 
        ++ret;
    }
 
    for (int i = 0; i < 4; i++)
        if (vertex[to][i] >= 2// 문제 조건
            return vertex[to][i];
    
    return INF;
}
 
int Find(int x)
{
    if (parent[x] == x) return x;
    return parent[x] = Find(parent[x]);
}
 
void Union(int a, int b)
{
    a = Find(a);
    b = Find(b);
 
    if (a < b) parent[b] = a;
    else parent[a] = b;
}
 
int main(void)
{
    //cin.tie(0);
    scanf("%d %d"&N, &M);
 
    for (int i = 0; i < N; i++for (int j = 0; j < M; j++)     scanf("%d"&m[i][j]);
 
    for (int i = 0; i < N; i++)
        for (int j = 0; j < M; j++)    
            if (!visit[i][j] && m[i][j])
            {
                dfs(i, j, gc);
                ++gc;
            }
 
    --gc;
 
    for (int i = 1; i <= gc; i++) parent[i] = i;
 
    // v[정점] = {해당 정점의 좌표들}
    for (int i = 0; i < N; i++)
        for (int j = 0; j < M; j++)
            if (m[i][j])
                v[m[i][j]].push_back({ i, j });    
 
    for (int i = 1; i <= gc; i++)
    {
        for (int j = i + 1; j <= gc; j++)
        {        
            int Min = INF; // 최솟값
            bool calc = false;
 
            for (auto pos : v[i])
            {
                int get = bfs(pos.first, pos.second, i, j);
 
                if (get >= 2 && get != INF)
                {
                    calc = true;
                    Min = min(Min, get);
                }
            }
 
            if (calc) mst.push_back({ Min, i, j });        
        }
    }
 
    // 간선의 가중치를 중심으로 오름차순(MST를 활용하기 위해)
    sort(mst.begin(), mst.end(), cmp);
 
    int cnt = 0;
 
    for (auto i : mst)
        if (Find(i.here) != Find(i.next))
        {
            Union(i.here, i.next);
            answer += i.w;
            ++cnt;
            if (cnt == gc - 1break;
        }
    
    // MST 특성상 간선의 개수는 전체 정점 - 1이 되어야 한다.
    printf("%d\n", cnt == gc - 1 ? answer : -1);
 
    return 0;
}
cs




















728x90
반응형

'알고리즘 문제 > BOJ' 카테고리의 다른 글

1092번 배  (0) 2019.09.28
2887번 행성 터널  (0) 2019.09.28
2842번 집배원 한상덕  (0) 2019.09.26
2606번 바이러스  (0) 2019.09.22
17471번 게리맨더링  (0) 2019.09.21