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

 

2352번: 반도체 설계

첫째 줄에 정수 n(1 ≤ n ≤ 40,000)이 주어진다. 다음 줄에는 차례로 1번 포트와 연결되어야 하는 포트 번호, 2번 포트와 연결되어야 하는 포트 번호, …, n번 포트와 연결되어야 하는 포트 번호가 주어진다. 이 수들은 1 이상 n 이하이며 서로 같은 수는 없다고 가정하자.

www.acmicpc.net

1번 포트부터 n번 포트까지 연결이 꼬이지 않게 최대로 연결해야 하는 문제로 최장 증가수열문제이다.

 

 

12015번 가장 긴 증가하는 부분 수열 2 문제(문제 링크)와 똑같은 코드로 nlogn만에 풀 수 있다.

(12015번 문제 풀이 링크)

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
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
 
int n;
vector<int> vt;
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n;
    vt.push_back(0);
 
 
 
    int num;
    for (int i = 0; i < n; i++) {
        cin >> num;
        if (num > vt.back()) {
            //벡터의 가장 마지막 값보다 크다면(증가하는 순서) push
            vt.push_back(num);
        }
        else {
            //num이상의 값이 처음으로 나타나는 위치에 num을 넣는다.
            int index = lower_bound(vt.begin(), vt.end(), num) - vt.begin();
            vt[index] = num;
        }
    }
 
 
 
    //처음에 넣어놓은 0을 제외한 벡터의 사이즈가 최장 증가 수열의 길이
    cout << vt.size() - 1 << '\n';
 
    return 0;
}
Colored by Color Scripter
 

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

 

12738번: 가장 긴 증가하는 부분 수열 3

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (-1,000,000,000 ≤ Ai ≤ 1,000,000,000)

www.acmicpc.net

12015번 가장 긴 증가하는 부분 수열 2 문제(문제 링크)와 거의 똑같은 LIS 문제이다. (12015번 문제 풀이 링크)

 

 

 

두 문제의 차이는 수열의 값의 범위뿐이다. 이번 문제에서는 범위가 (-1,000,000,000 ≤ Ai ≤ 1,000,000,000) 으로 처음에 벡터에 넣어놓는 값으로 -1,000,000,000보다 작은 값을 넣어놓기만 하면 된다.

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
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
 
int n;
vector<int> vt;
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n;
    vt.push_back(-2000000000);
 
 
 
    int num;
    for (int i = 0; i < n; i++) {
        cin >> num;
        if (num > vt.back()) {
            //벡터의 가장 마지막 값보다 크다면(증가하는 순서) push
            vt.push_back(num);
        }
        else {
            //num이상의 값이 처음으로 나타나는 위치에 num을 넣는다.
            int index = lower_bound(vt.begin(), vt.end(), num) - vt.begin();
            vt[index] = num;
        }
    }
 
 
 
    //처음에 넣어놓은 0을 제외한 벡터의 사이즈가 최장 증가 수열의 길이
    cout << vt.size() - 1 << '\n';
 
    return 0;
}
Colored by Color Scripter
 

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

 

12015번: 가장 긴 증가하는 부분 수열 2

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000,000)

www.acmicpc.net

LIS 문제이지만 11053번 가장 긴증가하는 부분 수열문제처럼 dp로 풀 수 없다.

dp로 풀면 시간 복잡도가 n제곱이 나오는데 이 문제는 n의 최댓값이 1,000,000이므로 n제곱으로 풀면 시간초과가 난다.

 

 

이분 탐색을 이용하여 nlogn만에 풀 수 있다.

 

 

0. 우선 입력으로 들어오는 최솟값보다 작은 값을 벡터에 넣어놓고 시작한다.

 

 

1. 값을 입력 받는다.

- 벡터의 맨 마지막 값보다 크다면(증가하는 순서라면) 입력받은 값을 벡터에 넣는다.

- 그렇지 않다면 이분탐색으로 벡터에 입력받은 값 이상의 값이 처음으로 나타나는 위치에 입력받은 값을 넣어준다.

(lower_bound를 이용하여 쉽게 구할 수 있다. )

 

 

2. 입력이 끝나면 벡터의 사이즈 -1한 값이 가장 긴 증가하는 부분 수열의 길이가 된다.

(처음에 넣어 놓은 값을 제외하기 위해 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
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
 
int n;
vector<int> vt;
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n;
    vt.push_back(0);
 
 
 
    int num;
    for (int i = 0; i < n; i++) {
        cin >> num;
        if (num > vt.back()) {
            //벡터의 가장 마지막 값보다 크다면(증가하는 순서) push
            vt.push_back(num);
        }
        else {
            //num이상의 값이 처음으로 나타나는 위치에 num을 넣는다.
            int index = lower_bound(vt.begin(), vt.end(), num) - vt.begin();
            vt[index] = num;
        }
    }
 
 
 
    //처음에 넣어놓은 0을 제외한 벡터의 사이즈가 최장 증가 수열의 길이
    cout << vt.size() - 1 << '\n';
 
    return 0;
}
hColored by Color Scripter
 

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

 

11053번: 가장 긴 증가하는 부분 수열

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이고, 길이는 4이다.

www.acmicpc.net

문제 제목 그대로 가장 긴 증가하는 부분 수열 LIS (Longest increasing Subsequence) 문제이다.

일반적으로 dp 식을 사용해서 풀 수 있다.

 

 

 

dp[i]는 수열의 i번째 값을 마지막으로 하는 가장 긴 증가하는 부분 수열이다.

dp[i]값을 구하기 위해서는 첫 번째 값부터 i-1번째 값( j )까지를 검사한다. -> 시간 복잡도는 n제곱이 된다

(먼저 dp배열은 자기 자신을 부분 수열로 하면 최소 길이가 1이므로 1로 초기화해준다)

 

 

 

1. j번 수열의 값이 i번 수열의 값보다 작고 (증가하는 수열이어야 하므로)

2. dp[j]값은 현재 dp[i]값보다 같거나 커야 한다(가장 긴 수열)

위의 조건을 만족한다면 dp[i] 는 dp[j] + 1 값을 가지게 된다(수열의 길이가 1 늘어남) 

 

 

 

문제에 나온 예시를 그림으로 보면 처음에 첫 번째 값(i가 0)인 10은 최대 길이가 1이므로 1인 상태로 다음 수열로 넘어간다.

 

 

 

 

 

수열의 다음 값(i가 1)은 20인데 앞의 값은 10 밖에 없다. 10은 20보다 작고 dp값도 같기 때문에 dp[i]값은 +1 된다.

 

 

 

그다음 값은 10인데 앞의 값들(10과 20) 중 10보다 작은 값이 없으므로 길이는 그대로 자기 자신인 1이 된다.

 

그리고 i는 증가해서 30 값을 가리킨다. 현재 i가 3이므로 0번부터 2(i-1) 번까지의 값을 보면 된다.

0번부터 2번까지의 값은 각각 10, 20, 10으로 모두 30보다 작다.

이중 dp배열의 가장 큰 값+1이 dp[i]값이 되므로 dp[1]값인 2에서 1을 더한 3이 dp[3]에 저장된다.

 

 

 

 

 

i값이 4가 되고 이제 j는 0부터 3까지의 값을 검사한다.

이 중 20보다 작은 값은 0번째와 2번째이다. 둘 다 10이고 dp의 값도 1로 현재 dp[i]와 같으므로 dp[i]는 dp[j]에 1을 더한 값인 2가 된다.

 

 

 

 

 

마지막 값인 50과 앞의 값들을 비교해보면 모두 50보다 작다. 그러므로 가장 큰 dp값인 dp[3] + 1 한 값인 4가 dp[5]에 저장된다.

 

 

 

 

마지막 값이 작은 값인 경우에는 마지막 위치에 최댓값이 저장되지 않을 수 있으므로 dp배열을 모두 검사해서 최댓값을 찾아야 한다.

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
#include <iostream>
using namespace std;
 
int arr[1000];
int dp[1000];
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    int n;
    cin >> n;
 
 
    for (int i = 0; i<n; i++cin >> arr[i];
 
 
    for (int i = 0; i<n; i++) {
        //처음엔 자기 자신의 길이인 1
        dp[i] = 1;
 
        //i번보다 앞의 값들과 비교
        for (int j = 0; j<i; j++) {
 
            //j번째 값이 i 번째 값보다 작아야 하고
            //dp값이 현재 가진 값보다 같거나 커야한다.
            if (arr[j] < arr[i] && dp[j] >= dp[i]) {
                //현재 i번째 값이 부분 수열에 추가되므로 길이는 +1이 된다.
                dp[i] = dp[j] + 1;
            }
        }
    }
 
 
    //최댓값을 찾는다.
    int ans = 0;
    for (int i = 0; i<n; i++) {
        if (ans < dp[i]) ans = dp[i];
    }
 
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 12015. 가장 긴 증가하는 부분 수열 2  (0) 2020.01.29
[BOJ] 1965. 상자넣기  (0) 2020.01.29
[BOJ] 2167. 2차원 배열의 합  (0) 2020.01.24
[BOJ] 2805. 나무 자르기  (0) 2020.01.21
[BOJ] 1654. 랜선 자르기  (0) 2020.01.20

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

 

5567번: 결혼식

문제 상근이는 자신의 결혼식에 학교 동기 중 자신의 친구와 친구의 친구를 초대하기로 했다. 상근이의 동기는 모두 N명이고, 이 학생들의 학번은 모두 1부터 N까지이다. 상근이의 학번은 1이다. 상근이는 동기들의 친구 관계를 모두 조사한 리스트를 가지고 있다. 이 리스트를 바탕으로 결혼식에 초대할 사람의 수를 구하는 프로그램을 작성하시오. 입력 첫째 줄에 상근이의 동기의 수 n (2 ≤ n ≤ 500)이 주어진다. 둘째 줄에는 리스트의 길이 m (1 ≤ m

www.acmicpc.net

먼저 친구관계를 인접 리스트로 구현해주고 입력받은 값이 상근이의 친구(1번과 친구)인 경우 바로 큐에 넣어주고 체크한다. 그럼 처음 큐의 사이즈는 상근이의 친구들 수만큼이 되므로 ans 변수(초대할 친구 수)에 큐 사이즈를 저장한다

 

 

그리고 상근이의 친구들에 대해서 친구를 조사해준다. 아직 check가 되지 않았다면 ans값\을 증가시킨다. 친구의 친구만 더해주면 되므로 친구의 친구는 큐에 새로 넣지 않는다.

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
#include<iostream>
#include <vector>
#include <queue>
using namespace std;
 
int n, m;
vector<int> list[501];
bool check[501];
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    queue<int> q;
    
    cin >> n >> m;
 
    int x, y;
    for (int i = 0; i<m; i++) {
        cin >> x >> y;
        list[x].push_back(y);
        list[y].push_back(x);
 
        //상근이의 친구를 큐에 넣는다.
        if (x == 1) {
            q.push(y);
            check[y] = true;
        }
        else if (y == 1) {
            q.push(x);
            check[x] = true;
        }
    }
 
    //현재 큐 사이즈가 상근이의 친구 수
    int ans = q.size();
    check[1= true;
    
 
    int size;
    while (!q.empty()) {
        x = q.front();
        q.pop();
 
        size = list[x].size();
        for (int k = 0; k < size; k++) {
            y = list[x][k];
            if (check[y]) continue;
 
            ans++;
            check[y] = true;
        }
    }
 
    cout << ans << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 1654. 랜선 자르기  (0) 2020.01.20
[BOJ] 10816. 숫자 카드 2  (0) 2020.01.20
[BOJ] 7662. 이중 우선순위 큐  (0) 2019.11.13
[BOJ] 17472. 다리 만들기 2  (0) 2019.10.16
[BOJ] 2146. 다리 만들기  (0) 2019.10.16

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

 

7662번: 이중 우선순위 큐

문제 이중 우선순위 큐(dual priority queue)는 전형적인 우선순위 큐처럼 데이터를 삽입, 삭제할 수 있는 자료 구조이다. 전형적인 큐와의 차이점은 데이터를 삭제할 때 연산(operation) 명령에 따라 우선순위가 가장 높은 데이터 또는 가장 낮은 데이터 중 하나를 삭제하는 점이다. 이중 우선순위 큐를 위해선 두 가지 연산이 사용되는데, 하나는 데이터를 삽입하는 연산이고 다른 하나는 데이터를 삭제하는 연산이다. 데이터를 삭제하는 연산은 또

www.acmicpc.net

stl에 있는 multiset을 사용하면 자동으로 정렬되기 때문에 쉽게 풀 수 있는 문제다.

set은 중복이 허용되지 않기때문에 multiset을 사용하여야한다.

 

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
#include <iostream>
#include <set>
using namespace std;
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    int T;
    cin >> T;
 
    int k;
    while (T--) {
        multiset<int> ms;
        cin >> k;
        
        while (k--) {
            char i;
            int num;
            cin >> i >> num;
            
            if (i == 'I') {
                ms.insert(num);
            }
            else if (i == 'D') {
                if (ms.empty()) continue;
 
 
                if (num == 1) {
                    //최댓값 삭제
                    ms.erase(--ms.end());
                }
                else {
                    //최솟값 삭제
                    ms.erase(ms.begin());
                }
            }
 
        }
 
        if (ms.empty()) cout << "EMPTY" << '\n';
        else cout << *(--ms.end()) << ' ' << *ms.begin() << "\n";
    }
    
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 10816. 숫자 카드 2  (0) 2020.01.20
[BOJ] 5567. 결혼식  (0) 2019.11.13
[BOJ] 17472. 다리 만들기 2  (0) 2019.10.16
[BOJ] 2146. 다리 만들기  (0) 2019.10.16
[BOJ] 17471. 게리맨더링  (0) 2019.10.16

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

 

17472번: 다리 만들기 2

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.

www.acmicpc.net

이 문제는 최소 신장 트리(MST)를 사용하여서 풀 수 있다. 완전 탐색으로도 그냥 풀 수 있다는데 다음에 풀어봐야겠다.

나는 크루스칼 알고리즘을 사용해서 풀었다.

 

 

1. 먼저 BFS로 각 섬에 번호를 붙인다.

2. 각 섬들 간 만들 수 있는 최단 거리의 직선 다리를 다시 BFS로 만들어 본다.

섬이 4개라고 했을 때, 1에서 2, 1에서 3, 1에서 4, 2에서 3, 2에서 4, 3에서 4로 가는 다리를 만들어주면 된다.

(다리 길이가 2 이상이 아니라면 만들 수 없다)

(직선 다리를 만들기 위해서 각 방향마다 쭉쭉 그 방향으로 이동한다.)

3. 다리를 만들 수 있다면 다리 길이와 함께 연결된 두 섬 번호를 벡터에 넣어준다.

4. 모든 다리를 구했으면 크루스칼 알고리즘을 이용하여 최소 스패닝 트리를 만들어준다.

 

 

크루스칼 알고리즘을 간단히 설명하면

간선들을 가중치를 기준으로 오름 차순 정렬한 후에

가중치가 제일 작은 것부터 연결되지 않은 간선들을 순차적으로 선택해나가는 알고리즘이다.

그리고 정점이 n 개라면 간선의 개수는 n-1개이므로 선택한 간선이 n-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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
 
int n, m;
int map[10][10];
int island[10][10];
int p[7];
vector<pair<intpair<intint>>> vt;
 
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
int getParent(int a) {
    if (p[a] == a) return a;
    return p[a] = getParent(p[a]);
}
 
 
void unionSet(int a, int b) {
    int rootA = getParent(a);
    int rootB = getParent(b);
    p[rootA] = rootB;
}
 
 
//u번 섬에서 v번 섬으로 가는 직선 최단 거리를 구한다.
void getMinDist(int u, int v) {
    queue < pair<intint>> q;
 
    //u번 섬에 있는 칸들을 모두 큐에 넣는다.
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (island[i][j] == u) {
                q.push(make_pair(i, j));
            }
        }
    }
 
    int mindist = 10;
    int x, y;
    while (!q.empty()) {
        x = q.front().first;
        y = q.front().second;
        q.pop();
 
        for (int k = 0; k < 4; k++) {
            int nx = x + dx[k];
            int ny = y + dy[k];
            int dist = 0;
 
            //한방향으로 계속 이동
            while (true) {
                //범위를 넘어간 경우
                if (nx < 0 || ny < 0 || nx >= n || ny >= m) break;
                //같은 섬인경우 더 탐색하지 않는다.
                if (island[nx][ny] == u) break;
 
                if (island[nx][ny] == v) {
                    //v번섬인 경우 최소거리와 비교후 더 탐색하지 않는다.
                    if (dist > 1 && mindist > dist) mindist = dist;
                    break;
                }
                else if (island[nx][ny] == 0) {
                    //바다인 경우 거리증가하고 이동
                    dist++;
                    nx += dx[k];
                    ny += dy[k];
                }
                else {
                    //다른 섬인 경우 더 탐색하지 않는다.
                    break;
                }
 
            }
        }
    }
 
    //최소거리가 초기값이 아니라면 벡터에 최소거리와 섬의 번호를 넣는다.
    if (mindist != 10) vt.push_back(make_pair(mindist, make_pair(u, v)));
}
 
 
void bfs(int x, int y, int num) {
    queue<pair<intint> > q;
    q.push(make_pair(x, y));
    island[x][y] = num;
 
    while (!q.empty()) {
        x = q.front().first;
        y = q.front().second;
        q.pop();
 
        for (int k = 0; k < 4; k++) {
            int nx = x + dx[k];
            int ny = y + dy[k];
            if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
            if (map[nx][ny] == 0continue;
            if (island[nx][ny] != 0continue;
 
            q.push(make_pair(nx, ny));
            island[nx][ny] = num;
        }
    }
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> map[i][j];
        }
    }
 
 
    //섬에 번호를 붙인다.
    int num = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            //바다인 경우
            if (map[i][j] == 0continue;
            //이미 번호를 붙인 경우
            if (island[i][j] != 0continue;
 
            bfs(i, j, ++num);
        }
    }
 
 
    //위에서 번호를 붙인 num이 섬의 개수
    //각 섬들간의 만들수 있는 다리를 모두 만든다.
    for (int i = 1; i <= num - 1; i++) {
        for (int j = i + 1; j <= num; j++) {
            getMinDist(i, j);
        }
    }
 
    int size = vt.size();
 
    //거리를 기준으로 오름차순 정렬
    sort(vt.begin(), vt.end());
 
    //부모 배열 초기화(처음 부모는 자기 자신)
    for (int i = 1; i <= num; i++) p[i] = i;
 
    int sum = 0;
    int cnt = 0;
    int index = 0;
 
    int u, v, dist;
    //선택한 간선의 개수가 섬의개수 -1이 될때까지 간선을 선택한다.
    while (cnt < num - 1) {
        if (index == size) {
            //모든 섬을 연결하는 것이 불가능하다
            cout << -1 << "\n";
            return 0;
        }
 
        dist = vt[index].first;
        u = vt[index].second.first;
        v = vt[index].second.second;
 
        index++;
        if (getParent(u) == getParent(v)) {
            //이미 연결되어 있다.
            continue;
        }
        else {
            //연결되어 있지 않다면 합친다.
            unionSet(u, v);
 
            //연결된 간선의 수 증가
            cnt++;
 
            //다리 길이에 합쳐준다.
            sum += dist;
        }
    }
 
 
    cout << sum << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 5567. 결혼식  (0) 2019.11.13
[BOJ] 7662. 이중 우선순위 큐  (0) 2019.11.13
[BOJ] 2146. 다리 만들기  (0) 2019.10.16
[BOJ] 17471. 게리맨더링  (0) 2019.10.16
[BOJ] 17141. 연구소 2  (0) 2019.09.06

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

 

2146번: 다리 만들기

여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은 섬을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다는 생각을 하게 되었다. 그래서 그는, 생색내는 식으로 한 섬과 다른 섬을 잇는 다리 하나만을 만들기로 하였고, 그 또한 다리를 가장 짧게 하여 돈을 아끼려 하였다. 이 나라는 N×N크기의 이차원 평면상에 존재한다. 이 나라는 여러 섬으로 이루어져 있으며, 섬이란 동서남북

www.acmicpc.net

먼저 BFS탐색으로 각 섬에 번호를 붙여준다. 이와 관련된 비슷한 문제는 단지 번호 붙이기 문제(풀이) 풀어보면 된다. 

 

 

번호를 붙였으면 바다가 아닌 모든 칸에서 다른 섬으로 가는 최단 거리를 다시 BFS로 구한다.

한칸씩 이동할 때마다 거리를 늘려주기 위해서 큐 사이즈만큼씩 진행한다.

큐 사이즈가 한번 이동했을 때 갈 수 있는 칸들이기 때문이다.

 

BFS를 할때에는

이동할 칸이 현재와 같은 섬인 경우 (같은 번호인 경우)는 이동하지 않는다.

이동할 칸이 바다라면 이동한다.

이동할 칸이 다른 섬이라면 그때까지의 거리를 최소거리와 비교하여 더 작다면 갱신해준다.

 

 

모든 칸에서 가능한 최소거리를 구해야 하므로 BFS로 리턴 받은 최소거리들 중 다시 최소거리가 정답이다.

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
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
 
 
int n;
int map[100][100];
int island[100][100];
bool check[100][100];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
 
//섬에 번호를 붙인다
void bfs(int x, int y, int num) {
    queue<pair<intint>> q;
    q.push(make_pair(x, y));
    island[x][y] = num;
 
    while (!q.empty()) {
        x = q.front().first;
        y = q.front().second;
        q.pop();
 
        for (int k = 0; k < 4; k++) {
            int nx = x + dx[k];
            int ny = y + dy[k];
            //범위 체크
            if (nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
            //바다인 경우
            if (map[nx][ny] == 0continue;
            //이미 번호를 붙인 경우
            if (island[nx][ny] != 0continue;
 
            q.push(make_pair(nx, ny));
            island[nx][ny] = num;
        }
    }
}
 
 
//최소 거리를 계산한다.
int bfs2(int x, int y, int now) {
    memset(check, falsesizeof(check));
 
    queue<pair<intint>> q;
    q.push(make_pair(x, y));
    check[x][y] = true;
 
    int dist = 0;
    int mindist = 100000;
    while (!q.empty()) {
        int size = q.size();
 
        while (size--) {
            x = q.front().first;
            y = q.front().second;
            q.pop();
 
            for (int k = 0; k < 4; k++) {
                int nx = x + dx[k];
                int ny = y + dy[k];
                //범위 체크
                if (nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
                //이동한 곳이 같은 섬인 경우
                if (island[nx][ny] == now) continue;
                //이미 방문한 경우
                if (check[nx][ny]) continue;
 
                //바다인 경우 일단 이동한다.
                if (island[nx][ny] == 0) {
                    q.push(make_pair(nx, ny));
                    check[nx][ny] = true;
                }
                else {
                    //바다도 아니고 현재 섬이 아닌 다른 섬이면 지금까지의 거리를 최소거리와 비교한다.
                    if (mindist > dist) mindist = dist;
                }
 
            }
        }
 
        //한번에 갈 수 있는 탐색이 끝나면 거리 증가
        dist++;
 
    }
 
    return mindist;
}
 
int main() {
 
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> map[i][j];
        }
    }
 
    //연결된 섬들에 번호를 붙인다.
    int num = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            //이미 번호가 붙은 경우
            if (island[i][j] != 0continue;
            //바다인 경우
            if (map[i][j] == 0continue;
 
            bfs(i, j, ++num);
        }
    }
 
 
    int ans = 100000;
    int tmp;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            //바다인 경우
            if (map[i][j] == 0continue;
 
            //(i,j)에서 갈 수 있는 가장 가까운 다른 섬까지의 거리를 tmp에 저장
            tmp = bfs2(i, j, island[i][j]);
            //최솟값과 비교
            if (ans > tmp) ans = tmp;
        }
    }
 
    cout << ans << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 7662. 이중 우선순위 큐  (0) 2019.11.13
[BOJ] 17472. 다리 만들기 2  (0) 2019.10.16
[BOJ] 17471. 게리맨더링  (0) 2019.10.16
[BOJ] 17141. 연구소 2  (0) 2019.09.06
[BOJ] 6359. 만취한 상범  (0) 2019.09.04

+ Recent posts