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

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

 

17471번: 게리맨더링

선거구를 [1, 4], [2, 3, 5, 6]으로 나누면 각 선거구의 인구는 9, 8이 된다. 인구 차이는 1이고, 이 값보다 더 작은 값으로 선거구를 나눌 수는 없다.

www.acmicpc.net

 

먼저 그래프가 연결된 정점 정보로 주어지므로 vector 배열을 사용해서 인접 리스트를 구현해주었다.

adjlist[x] 에는 x에 연결된 정점들을 push해준다.

 

 

 

인구 차이가 가장 적게 나도록 선거구를 나누기 위해서는

DFS로 각 구역들을 선거구 1에 넣는 경우 선거구 2에 넣는 경우모두 구해서 계산해본다.

선거구에 넣을 때, 나중에 BFS탐색 시 연결되어 있는지 확인하기 위해서 check배열을 추가적으로 사용하였다.

선거구 1에 넣는 경우는 check배열의 값을 true선거구 2에 넣는 경우는 check배열의 값을 false로 하였다.

 

 

 

각각의 경우에서 BFS탐색으로 한 선거구에 포함되어 있는 구역이 모두 연결되어 있는지 검사해줘야 하는데, 선거구 1과 선거구 2를 각각 BFS로 검사해줬다.

 

 

 

먼저 선거구 1을 검사할 때는 선거구에 있는 지역 하나를 처음에 큐에 넣고 연결된 구역들을 방문하며 방문 체크를 한다.

이때, check배열의 값이 false인 경우(선거구 2인 경우) 방문하지 못한다. BFS탐색이 끝난 후에 같은 선거구인데 방문하지 않았다면 한 선거구 내에 연결되지 않은 구역이 있다는 뜻이므로 false를 리턴한다.

 

 

 

마찬가지로 선거구 2를 검사할 때에도 check배열의 값이 true인 경우 (선거구 1인 경우)를 방문하지 않고 연결된 정점들을 모두 방문한다. 그리고 선거구 내에 방문하지 않은 구역이 있다면 false를 리턴한다.

 

 

 

 

위의 두 가지 경우에서 모두 false를 리턴하지 않았다면 두 선거구가 모두 각 선거구 내의 구역들이 연결되어 있다는 뜻이므로 true를 리턴하고 인구 차이를 계산한다.

 

 

 

 

/*구역의 수가 최대 10개이고, 각 구역의 인구수가 최대 100명이므로 두 구역의 인구 차이가 최대 1000명을 넘지 않으므로 정답을 저장할 ans변수의 초기값을 1000으로 해두었다 */

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
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
 
int n;
int ans;
 
//인구 수 저장
int arr[11];
 
//인접리스트
vector<int> adjlist[11];
 
//선거구 1
vector<int> vt1;
 
//선거구 2
vector<int> vt2;
 
//선거구 1인 경우 true, 선거구 2인 경우 false
bool check[11];
 
//BFS할때 방문체크 배열
bool visit[11];
 
 
bool bfs() {
    //방문배열 초기화
    for (int i = 0; i <= n; i++) visit[i] = false;
 
    //선거구 1이 모두 연결되어 있는지 검사
    queue<int> q;
    q.push(vt1[0]);
    visit[vt1[0]] = true;
 
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        
        //현재 구역과 연결된 구역들을 검사한다
        int size = adjlist[x].size();
        for (int k = 0; k < size; k++) {
            int nx = adjlist[x][k];
            //이미 방문한 경우
            if (visit[nx]) continue;
            //선거구 2인 경우
            if (!check[nx]) continue;
 
            visit[nx] = true;
            q.push(nx);
        }
    }
 
    //선거구 1에 연결되지 않은 구역(방문하지 못한 구역)이 있다면 false를 리턴
    for (int x : vt1) {
        if (!visit[x]) return false;
    }
 
 
    //방문배열 초기화
    for (int i = 0; i <= n; i++) visit[i] = false;
    
    //선거구 2가 연결되어 있는지 검사
    q.push(vt2[0]);
    visit[vt2[0]] = true;
 
    while (!q.empty()) {
        int x = q.front();
        q.pop();
 
        int size = adjlist[x].size();
        for (int k = 0; k < size; k++) {
            int nx = adjlist[x][k];
            if (visit[nx]) continue;
            //선거구 1인경우
            if (check[nx]) continue;
 
            visit[nx] = true;
            q.push(nx);
        }
    }
 
    //선거구 2에 연결되지 않은 구역(방문하지 못한 구역)이 있다면 false를 리턴
    for (int x : vt2) {
        if (!visit[x]) return false;
    }
 
    //각 선거구가 모두 연결되어 있다면 true를 리턴 
    return true;
}
 
 
void calc() {
    if (bfs()) {//각 선거구가 모두 연결되어 있다면 인구차이를 계산
        int sum = 0;
 
        //선거구 1과 선거구 2의 인구차이를 구한다.
        for (int x : vt1) sum += arr[x];
        for (int x : vt2) sum -= arr[x];
        if (sum < 0) sum = -sum;
 
        //최솟값보다 적다면 최솟값 갱신
        if (ans > sum) ans = sum;
    }
 
 
}
 
 
void solve(int index) {
    if (index > n) {
        //선거구에는 최소 1개의 구역이 포함되어야 한다
        if (vt1.size() == 0 || vt2.size() == 0return;
        
        //인구차이를 계산
        calc();
        return;
    }
 
    //index구역을 선거구 1에 넣는 경우
    vt1.push_back(index);
    check[index] = true;
    solve(index + 1);
    check[index] = false;
    vt1.pop_back();
 
    //index구역을 선거구 1에 넣는 경우
    vt2.push_back(index);
    solve(index + 1);
    vt2.pop_back();
 
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n;
    for (int i = 1; i <= n; i++cin >> arr[i];
 
    int cnt, x;
    for (int i = 1; i <= n; i++) {
        cin >> cnt;
        for (int j = 0; j < cnt; j++) {
            cin >> x;
            adjlist[i].push_back(x);
        }
        
    }
 
    ans = 1000;
 
    solve(1);
 
    //ans값이 갱신되지 않았다면 두 선거구로 나눌수없는 경우이다.
    if(ans != 1000cout << ans << '\n';
    else cout << -1 << '\n';
 
    return 0;
}
Colored by Color Scripter
 

 

'BOJ' 카테고리의 다른 글

[BOJ] 17472. 다리 만들기 2  (0) 2019.10.16
[BOJ] 2146. 다리 만들기  (0) 2019.10.16
[BOJ] 17141. 연구소 2  (0) 2019.09.06
[BOJ] 6359. 만취한 상범  (0) 2019.09.04
[BOJ] 1931. 회의실배정  (0) 2019.09.02

https://programmers.co.kr/learn/courses/30/lessons/42839

 

코딩테스트 연습 - 소수 찾기 | 프로그래머스

한자리 숫자가 적힌 종이 조각이 흩어져있습니다. 흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다. 각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때, 종이 조각으로 만들 수 있는 소수가 몇 개인지 return 하도록 solution 함수를 완성해주세요. 제한사항 numbers는 길이 1 이상 7 이하인 문자열입니다. numbers는 0~9까지 숫자만으로 이루어져 있습니다. 013은 0, 1, 3 숫자가 적힌 종이

programmers.co.kr

문자열로 주어지는 numbers의 길이가 7이하이기 때문에 완전탐색으로 풀 수 있다.

문제 카테고리도 완전 탐색이어서 바로 알 수 있긴하다...

 

1. 먼저 에라토스테네스의 체를 이용해서 소수를 모두 구해놓는다. 숫자 i가 소수라면 check[i]는 false값을 갖는다.

2. numbers의 각 자리의 값을 int형으로 변환하여 벡터에 넣어줬다.

3. 벡터에 넣어준 값들로 만들 수 있는 값들을 모두 구해서 set에 넣어준다(중복을 없애기 위해서)

4. 마지막으로 set에 있는 값들을 검사하여 소수이면 answer값을 증가시킨다.

 

 

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
#include <string>
#include <vector>
#include <set>
#define MAX 10000000
using namespace std;
 
bool check[MAX+1];
bool used[10];
set<int> st;
vector<int> vt;
 
void getPrime() {
    check[0= true;
    check[1= true;
    for(int i=2; i*i<=MAX; i++) {
        if(check[i]) continue;
        for(int j = i+i; j<=MAX; j+=i) {
            check[j] = true;
        }
    }
}
 
int n;
void select(int index, int num) {
    if(index == n) {
        //n가지 종이조각을 모두 사용하였다.
        return;
    }
    
    int newnum;
    
    //n가지의 종이조각으로 만들 수 있는 모든 수를 구한다.
    for(int i=0; i<n; i++) {
        //i번째 종이 조각을 사용하였는지 검사
        if(used[i]) continue;
        
        //i번째 종이 조각을 사용한다.
        used[i] = true;
 
        //이전의 숫자와 i번째 숫자를 합쳐준다.
        newnum = num*10+vt[i];
        
        //set에 넣고
        st.insert(newnum);
        
        //현재 만들어진 숫자에 다른 숫자를 더 붙이는 경우 탐색
        select(index+1, newnum);
 
        //다른 종이를 선택하는 경우를 위해서 다시 false값으로 변경
        used[i] = false;
    }
}
 
int solution(string numbers) {
    int answer = 0;
    n = numbers.size();
    
    //소수를 찾는다.(소수이면 check배열 값이 true)
    getPrime();
 
    
    //int로 바꿔서 vector에 넣는다.
    for(char c : numbers) {
        vt.push_back(c-'0');
    }
 
    
    //만들 수 있는 숫자 조합을 중복없이 구한다.
    select(0,0);
    
    //set에 들어가있는 값이 소수이면 answer값 증가
    for(auto iter=st.begin(); iter!=st.end(); iter++) {
        if(!check[*iter]) answer++;
    }
    
    return answer;
}
Colored by Color Scripter
 

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

 

17141번: 연구소 2

인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 승원이는 연구소의 특정 위치에 바이러스 M개를 놓을 것이고, 승원이의 신호와 동시에 바이러스는 퍼지게 된다. 연구소는 크기가 N×N인 정사각형으로 나타낼 수 있으며, 정사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다. 일부 빈 칸은 바이러스를 놓을 수 있는 칸이다. 바이러스는 상하좌우로

www.acmicpc.net

처음에 입력을 받을 때 바이러스를 놓을 수 있는 칸(값이 2인 칸)을 벡터에 모두 넣어놓는다.

그리고 이 칸들 중 m개를 선택하여 바이러스 퍼트리는 경우를 모두 해보고 최소 시간을 구한다.

 

 

m개를 모두 골라줬다면 골라준 자리에 바이러스를 넣고 퍼뜨린다.

바이러스를 퍼트리기 위해서 bfs를 사용하고 시간을 구하기 위해서 큐 사이즈만큼 단계별로 진행하였다.

그리고 각 큐 사이즈만큼 시작할 때마다 시간을 1씩 증가시켜서 시간을 구할 수 있다.

이렇게 하면 나중에 큐가 비었을 때도 한 번 더 1이 더해지므로 처음에 시간을 -1부터 시작하든지 나중에 1을 빼주던지 하면 된다.

 

 

바이러스를 모두 퍼뜨렸다면 퍼지는데 걸리는 시간을 리턴해주면 되는데,

모든 빈칸에 바이러스를 퍼졌는지 먼저 검사해주어야 한다.

빈칸인데 바이러스가 퍼져있지 않다면 문제 조건에 따라 시간 대신에 -1을 리턴한다.

 

 

그리고 시간의 최솟값을 구하기 위해서 먼저 정답을 저장할 ans에다가 -1을 초기값으로 넣어놓 ans가 -1인 경우 -1이 아닌경우를 나눠서 최솟값을 구해줬다.

먼저 -1인 경우에는 ans가 초기값이거나 bfs의 결괏값이 -1인 경우이다. 이때는 그냥 새로 bfs 한 결과를 바로 넣어주면 된다. -1이 아닌 경우에는 bfs의 결과가 -1이 아닌 ans보다 작은 값이 어야 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
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
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
 
int n, m;
int ans;
int map[50][50];
bool check[50][50];
vector<pair<int,int> > vt;
vector<int> virus;
 
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
int bfs() {
    queue<pair<intint> > q;
    memset(check, falsesizeof(check));
 
    int x, y;
    //선택한 바이러스를 놓을 수 있는 칸에 바이러스를 놓는다.
    for (int i = 0; i < m; i++) {
        x = vt[virus[i]].first;
        y = vt[virus[i]].second;
 
        q.push(make_pair(x,y));
        check[x][y] = true;
    }
 
 
    int time = -1;
    while (!q.empty()) {
 
        int size = q.size();
        time++;
 
        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 (check[nx][ny]) continue;
                //벽인경우
                if (map[nx][ny] == 1continue;
 
                q.push(make_pair(nx, ny));
                check[nx][ny] = true;
 
            }
        }
        
    }
 
    //바이러스를 어떻게 놓아도 모든 빈 칸에 바이러스를 퍼뜨릴 수 없는 경우
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            //빈칸인데 바이러스가 퍼져있지 않다
            if (map[i][j] == 0 && !check[i][j]) return -1;
        }
    }
 
    return time;
 
}
 
void select(int index, int cnt) {
    if (cnt == m) {
        int tmp = bfs();
        if (ans == -1) ans = tmp;
        else {
            if (tmp != -1 && ans > tmp) ans = tmp;
        }
 
        return;
    }
    if (index == vt.size()) {
        return;
    }
 
 
    //index번째 바이러스를 놓는 경우
    virus.push_back(index);
    select(index + 1, cnt+1);
    virus.pop_back();
 
    //바이러스를 놓지 않는 경우
    select(index + 1, cnt);
 
}
 
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 < n; j++) {
            cin >> map[i][j];
            //바이러스를 놓을 수 있는 칸이면 벡터에 넣는다.
            if (map[i][j] == 2) vt.push_back(make_pair(i, j));
        }
    }
 
    ans = -1;
 
    select(00);
    
    cout << ans << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 2146. 다리 만들기  (0) 2019.10.16
[BOJ] 17471. 게리맨더링  (0) 2019.10.16
[BOJ] 6359. 만취한 상범  (0) 2019.09.04
[BOJ] 1931. 회의실배정  (0) 2019.09.02
[BOJ] 2217. 로프  (0) 2019.09.02

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

 

6359번: 만취한 상범

문제 서강대학교 곤자가 기숙사의 지하에는 n개의 방이 일렬로 늘어선 감옥이 있다. 각 방에는 벌점을 많이 받은 학생이 구금되어있다. 그러던 어느 날, 감옥 간수인 상범이는 지루한 나머지 정신나간 게임을 하기로 결정했다. 게임의 첫 번째 라운드에서 상범이는 위스키를 한 잔 들이키고, 달려가며 감옥을 한 개씩 모두 연다. 그 다음 라운드에서는 2, 4, 6, ... 번 방을 다시 잠그고, 세 번째 라운드에서는 3, 6, 9, ... 번 방이 열려있으면 잠그고

www.acmicpc.net

먼저 상범이는 첫 번째 라운드에서 모든 방의 문을 열고, 그다음 라운드부터는 

k번째 라운드에서는 번호가 k의 배수인 방이 열려 있으면 잠그고, 잠겨 있다면 연다.

이런 식으로 n번째 라운드까지 진행한다.

 

문제의 입력으로 테스트 케이스와 함께 n값이 주어진다. n값은 방의 개수이자 라운드 진행 수이다.

 

이 문제는 2번째 라운드에서 2의 배수인 방들은 문이 잠겨져 있고 나머지 방들은 열려있는 상태에서

3번째 라운드를 진행한다.

즉, i번째 라운드의 상태에서 i+1번째 라운드의 상태로 바뀌므로 DP(Dynamic Programming)로 해결할 수 있다.

 

n(5 ≤ n ≤ 100)

n의 범위가 위와 같으므로 n이 100인 경우까지 모두 미리 구해놓으면 된다.

DP 식은 다음과 같이 정의할 수 있다.

 

dp[i][j] = true (i번째라운드의 j번 방이 열려있다)

dp[i][j] = false (i번째라운드의 j번 방이 잠겨져 있다)

 

위의 식을 이용하여 1번째라운드부터 n번째라운드까지 순차적으로 구할 수 있다.

dp배열을 모두 구해놨다면 n이 주어졌을 때, dp[n][i] (1<= i <= n) 중 true인 값들(열려있는 방)의 개수를 구해주면 된다.

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
#include<iostream>
#include <cstring>
using namespace std;
 
bool d[101][101];
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
 
    //열리면 true 잠기면 false
    //1번째 라운드에서 모든 방을 연다. (true로 초기화)
    memset(d[1], truesizeof(d[1]));
    
 
 
    //i번째 라운드에서 j번 방 문을 열거나 잠근다.
    for (int i = 2; i <= 100; i++) {
 
        //이전 방 문의 상태를 복사
        for (int j = 1; j <= 100; j++) {
            d[i][j] = d[i - 1][j];
        }
 
        //i의 배수인 방 문만 열거나 잠근다.
        for (int j = i; j <= 100; j += i) {
            if (d[i - 1][j]) d[i][j] = false;
            else d[i][j] = true;
        }
    }
 
 
    int T;
    cin >> T;
 
    int n;
    while (T--) {
        cin >> n;
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            //문이 열려있다
            if (d[n][i]) cnt++;
        }
 
        cout << cnt << '\n';
    }
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 17471. 게리맨더링  (0) 2019.10.16
[BOJ] 17141. 연구소 2  (0) 2019.09.06
[BOJ] 1931. 회의실배정  (0) 2019.09.02
[BOJ] 2217. 로프  (0) 2019.09.02
[BOJ] 1541. 잃어버린 괄호  (0) 2019.09.02

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

 

1931번: 회의실배정

(1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다.

www.acmicpc.net

각 회의가 겹치지 않게 하면서 회의실을 사용할 수 있는 최대 회의 수를 찾으면 된다.

 

 

문제에서 주어진 예제만 봐도 회의 진행시간이 짧은 순서로 진행하면 안 된다는 것을 알 수 있다. 물론 시작하는 시간이 빠른 것도 안된다. 한참 헤매다가 끝나는 시간을 기준으로 하면 된다는 것을 깨달았다...

 

 

끝나는 시간을 오름차순으로 정렬하여서 먼저 끝나는 회의를 진행하고 그 회의 바로 다음에 할 수 있는 끝나는 시간이 가장 빠른 회의를 진행하면 된다.

 

 

나는 벡터에 시작시간과 끝나는 시간을 pair로 넣어서 정렬했다.

pair는 first값을 기준으로 먼저 정렬되고 그다음으로 second값을 기준으로 정렬되기 때문에 

끝나는 시간을 앞에 넣어주었다. 그러면 끝나는 시간이 빠른 순으로 정렬된다.

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>
#include <algorithm>
#include <vector>
using namespace std;
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    int n;
    int s, e;
    vector<pair<intint> > vt;
 
    cin >> n;
 
    for (int i = 0; i < n; i++) {
        cin >> s >> e;
        vt.push_back(make_pair(e, s));
    }
 
 
    sort(vt.begin(), vt.end());
 
 
    //끝나는 시간
    int now = vt[0].first;
    
    //회의 수
    int ans = 1;
 
    for (int i = 1; i < n; i++) {
        //현재 회의가 끝나는 시간 보다 빨리 시작하는 회의는 진행하지 못한다.
        if (vt[i].second < now) continue;
 
        //다음 회의가 끝나는 시간
        now = vt[i].first;
        ans++;
    }
 
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 17141. 연구소 2  (0) 2019.09.06
[BOJ] 6359. 만취한 상범  (0) 2019.09.04
[BOJ] 2217. 로프  (0) 2019.09.02
[BOJ] 1541. 잃어버린 괄호  (0) 2019.09.02
[BOJ] 11729. 하노이 탑 이동 순서  (0) 2019.08.31

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

 

2217번: 로프

N(1≤N≤100,000)개의 로프가 있다. 이 로프를 이용하여 이런 저런 물체를 들어올릴 수 있다. 각각의 로프는 그 굵기나 길이가 다르기 때문에 들 수 있는 물체의 중량이 서로 다를 수도 있다. 하지만 여러 개의 로프를 병렬로 연결하면 각각의 로프에 걸리는 중량을 나눌 수 있다. k개의 로프를 사용하여 중량이 w인 물체를 들어올릴 때, 각각의 로프에는 모두 고르게 w/k 만큼의 중량이 걸리게 된다. 각 로프들에 대한 정보가 주어졌을 때, 이 로프들을

www.acmicpc.net

로프를 k개 (1 <= k <= n) 만큼 사용하는 경우 로프들을 이용해서 들 수 있는 중량을 모두 구해서 그중 최댓값이 최대 중량이 된다.

 

 

먼저 1개만 사용하는 경우에는 들 수 있는 중량이 가장 큰 로프의 경우가 최댓값이다.

그리고 2개만 사용하는 경우에는 들 수 있는 중량이 가장 큰 로프와 두번째로 큰 로프 두 개를 사용하여야 들 수 있는 최대 중량을 만들 수 있을 것이다. 이런식으로 k개의 로프를 사용하는 경우에는 들 수 있는 중량이 가장 큰 로프 k개를 사용하는 것이 최댓값이므로 로프를 내림차순으로 정렬해준다.

 

 

그리고 문제에서

 k개의 로프를 사용하여 중량이 w인 물체를 들어 올릴 때, 각각의 로프에는 모두 고르게 w/k 만큼의 중량이 걸리게 된다.

라고 하였으므로 k개를 사용할 때는 (가장 작은 로프의 값 X k) 만큼이 들어올릴 수 있는 중량이다.

이렇게 k개를 사용하는 모든 경우의 중량 중 최대 중량이 정답이다.

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>
using namespace std;
 
int arr[100000];
 
bool comp(int a, int b) {
    //내림차순
    return a > b;
}
 
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];
    }
 
    //내림차순 정렬
    sort(arr, arr + n, comp);
 
    //처음에 가장 큰 값(0번째 값)을 저장
    int ans = arr[0];
 
    for (int i = 1; i < n; i++) {
        if (ans < (arr[i] * (i + 1))) {
            ans = (arr[i] * (i + 1));
        }
    }
 
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 6359. 만취한 상범  (0) 2019.09.04
[BOJ] 1931. 회의실배정  (0) 2019.09.02
[BOJ] 1541. 잃어버린 괄호  (0) 2019.09.02
[BOJ] 11729. 하노이 탑 이동 순서  (0) 2019.08.31
[BOJ] 별 찍기 - 10  (0) 2019.08.31

+ Recent posts