반응형

문제 링크: www.acmicpc.net/problem/1197

 

1197번: 최소 스패닝 트리

첫째 줄에 정점의 개수 V(1 ≤ V ≤ 10,000)와 간선의 개수 E(1 ≤ E ≤ 100,000)가 주어진다. 다음 E개의 줄에는 각 간선에 대한 정보를 나타내는 세 정수 A, B, C가 주어진다. 이는 A번 정점과 B번 정점이 �

www.acmicpc.net

<문제 풀이>

 

Union-Find 알고리즘을 이용해서 기본적인 크루스칼 알고리즘을 구현하면 되는 문제입니다.

 

 

<C++ 소스 코드> 최소 스패닝 트리, 크루스칼 알고리즘

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
#include <iostream>
#include <algorithm>
#include <utility>
#include <vector>
 
using namespace std;
 
pair<intpair<intint> > edge[100000];
 
int parents[10001];
 
 
int v, e;
 
int Find(int x){
    if(x == parents[x]) return x;
    else return parents[x] = Find(parents[x]);
}
 
 
void Union(int x, int y){
    int xParents = Find(x);
    int yParents = Find(y);
    parents[xParents] = yParents;
}
 
 
 
 
int solution(){
    int res = 0;
    int cnt = 0;
    sort(edge, edge + e);
    for(int i = 0 ; i < e; i++){
        int c = edge[i].first;
        int a = edge[i].second.first;
        int b = edge[i].second.second;
        if(Find(a) == Find(b)) continue// 같은 그래프이면 무시
        Union(a, b); // a, b를 같은 그래프로 만듬
        res += c;
        cnt++;
        if(cnt == v - 1break// 최소 스패닝 트리를 만들면 종료
    }
    return res;
}
 
int main(void) {
    cin >> v >> e;
    for(int i = 1; i<=v; i++) parents[i] = i; // 처음엔 자기 자신이 부모
    for(int i = 0; i < e; i++){
        int a, b, c;
        cin >> a >> b >> c;
        edge[i].first = c;
        edge[i].second.first = a;
        edge[i].second.second = b;
    }
    
    cout<<solution();
    return 0;
}
cs

 

반응형

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[백준 3273번] 두 수의 합  (0) 2021.10.06
[백준 10871번] X보다 작은 수  (0) 2021.10.02
[백준 4256번] 트리  (0) 2020.07.18
[백준 10974번] 모든 순열  (0) 2020.07.05
[백준 2623번] 음악프로그램  (0) 2020.04.11
반응형

문제 링크: www.acmicpc.net/problem/4256

 

4256번: 트리

문제 이진 트리는 매우 중요한 기본 자료 구조이다. 아래 그림은 루트 노드가 유일한 이진 트리이다. 모든 노드는 최대 2개의 자식 노드를 가질 수 있으며, 왼쪽 자식이 순서가 먼저이다. 노드 n��

www.acmicpc.net

<문제 풀이> DFS(깊이 우선 탐색), 트리

전위 순회 결과 3 6 5 4 8 7 1 2 

중위 순회 결과 5 6 8 4 3 1 2 7

중위 순회 결과를 통해서 각 노드의 순서를 알 수 있고, 왼쪽 서브 트리와 오른쪽 서브 트리에 어떤 노드가 존재하는지 알 수 있습니다.

그리고 전위 순회 결과를 통해 규칙을 찾아볼 수 있는데, 크기가 n인 배열에 전위 순회 결과가 차례로 저장돼 있다고 했을 때 인덱스 + 1을 하면 왼쪽 서브 트리의 루트를 가리키게 할 수 있습니다.

예를 들어 arr [] = {3 ,6, 5, 4, 8, 7, 1, 2]인 배열이 있습니다. 이때

root == 3, index = 0 되고, 왼쪽 서브 트리로 이동하면

root == 6, index = 1 이 됩니다.

 

그리고 오른쪽 서브트리로 이동하는 방법은 왼쪽 서브 트리의 크기 + 1 만큼 인덱스를 더해주면 됩니다.

 

root == 3, index = 0

root == 7, index = 0 + 4 + 1 == 5

 

 

 

<C++ 소스 코드>

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
#include <iostream>
#include <vector>
using namespace std;
 
int preorder[1001];
int inorder[1001];
 
 
void solution(int root, int s, int e){
    for(int i = s ; i < e; i++){
        if(inorder[i] == preorder[root]){
            solution(root + 1, s, i); // left sub tree
            solution(root + i + 1 - s, i + 1, e); // right sub tree
            cout<<preorder[root]<<" ";
        }
    }
}
 
int main(void) {
    int test_case; 
    cin>>test_case;
    while(test_case--){
        int n ; cin>>n;
        for(int i=0; i<n; i++){
            cin >> preorder[i];
        }
        for(int i=0; i<n; i++){
            cin >> inorder[i];
        }
        solution(00, n);
        cout<<'\n';
    }
    return 0;
}
cs

 

 

 

반응형
반응형

문제 링크: https://www.acmicpc.net/problem/10974

 

10974번: 모든 순열

N이 주어졌을 때, 1부터 N까지의 수로 이루어진 순열을 사전순으로 출력하는 프로그램을 작성하시오.

www.acmicpc.net

<문제 풀이> 순열

백트래킹을 생각하면 쉽게 모든 순열을 구할 수 있다.

 

<C++ 소스코드>

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
#include <iostream>
#include <vector>
using namespace std;
 
#define MAX 8
bool visited[MAX + 1];
vector<int> elements;
int n;
void permutaiotn(){
    if(elements.size() == n){
        for(auto e : elements){
            cout<<e<<" ";
        }
        cout<<"\n";
    }else{
        for(int i=1; i<=n; i++){
            if(visited[i])continue;
            visited[i] = true;
            elements.push_back(i);
            permutaiotn();
            visited[i] = false;
            elements.pop_back();
        }
    }
 
}
 
int main(void) {
    cin >> n;
    permutaiotn();
    return 0;
}
cs
반응형
반응형

문제 링크: https://www.acmicpc.net/problem/2623

 

2623번: 음악프로그램

첫째 줄에는 가수의 수 N과 보조 PD의 수 M이 주어진다. 가수는 번호 1, 2,…,N 으로 표시한다. 둘째 줄부터 각 보조 PD가 정한 순서들이 한 줄에 하나씩 나온다. 각 줄의 맨 앞에는 보조 PD가 담당한 가수의 수가 나오고, 그 뒤로는 그 가수들의 순서가 나온다. N은 1이상 1,000이하의 정수이고, M은 1이상 100이하의 정수이다.

www.acmicpc.net

<문제 풀이> 위상 정렬

 

위상 정렬 알고리즘을 그대로 구현하면 되는데, 입력받은 순서를 그래프로 표현했을 때 사이클이 존재하면 0을 출력해합니다.

 

위상 정렬했을 때 결과 리스트의 크기가 정점의 개수 n과 다르면 사이클이 존재합니다.

 

<C++ 소스 코드>

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
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
 
int n, m;
vector<int> adj[1001];
vector<int> res;
int indegree[1001];
void solve() {
    queue<int> q;
    for (int i = 1; i <= n; i++if (!indegree[i]) q.push(i);
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        res.push_back(cur);
        for (auto& next : adj[cur]) {
            indegree[next]--;
            if (!indegree[next]) q.push(next);
            
        }
    }
    if (res.size() != n) { //사이클이 존재하면
        res.clear();
        res.push_back(0);
    }
}
 
int main(void) {
    ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
 
    cin >> n >> m;
 
    while (m--) {
        int num;
        cin >> num;
        int prev;
        cin >> prev;
        num--;
        while (num--) {
            int cur;
            cin >> cur;
            adj[prev].push_back(cur);
            indegree[cur]++;
            prev = cur;
        }
    }
    solve();
    for (auto& e : res) cout << e << '\n';
 
    return 0;
}
cs

 

반응형
반응형

문제 링크: https://www.acmicpc.net/problem/2252

 

2252번: 줄 세우기

첫째 줄에 N(1≤N≤32,000), M(1≤M≤100,000)이 주어진다. M은 키를 비교한 회수이다. 다음 M개의 줄에는 키를 비교한 두 학생의 번호 A, B가 주어진다. 이는 학생 A가 학생 B의 앞에 서야 한다는 의미이다. 학생들의 번호는 1번부터 N번이다.

www.acmicpc.net

<문제 풀이> 그래프, 위상 정렬

위상 정렬 알고리즘을 그대로 적용하면 되는 문제입니다.

 

1. indegree가 0인 정점을 모두 큐에 넣는다.

2. 큐가 빌 때까지 다음을 반복한다.

3. 큐에서 정점을 하나 꺼낸다.

4. 꺼낸 정점을 결과 리스트에 넣고 정점의 인접 리스트를 확인한다.

5. 꺼낸 정점과 연결된 다른 정점의 indegree을 1 감소시킨다.

5. 만약 연결된 정점의 indegree를 1 감소시켰을 때 indegree가 0이 되면 그 정점을 큐에 넣는다. 

 

<C++ 소스 코드>

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
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
 
int indegree[32001];
vector<int> adj[32001];
vector<int> res;
int n, m;
 
void solve() {
    queue<int> q;
    for (int i = 1; i <= n; i++if (!indegree[i]) q.push(i);
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        res.push_back(cur);
        for (auto& next : adj[cur]) {
            indegree[next]--;
            if (!indegree[next])q.push(next);
        }
    }
    
}
 
int main(void) {
    ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
 
    cin >> n >> m;
 
    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        adj[a].push_back(b);
        indegree[b]++;
    }
    solve();
 
    for (auto& e : res) {
        cout << e << " ";
    }
 
 
    return 0;
}
cs
 

 

반응형
반응형

문제 링크: https://www.acmicpc.net/problem/2250

 

2250번: 트리의 높이와 너비

첫째 줄에 노드의 개수를 나타내는 정수 N(1 ≤ N ≤ 10,000)이 주어진다. 다음 N개의 줄에는 각 줄마다 노드 번호와 해당 노드의 왼쪽 자식 노드와 오른쪽 자식 노드의 번호가 순서대로 주어진다. 노드들의 번호는 1부터 N까지이며, 자식이 없는 경우에는 자식 노드의 번호에 -1이 주어진다.

www.acmicpc.net

<문제 풀이> 트리

트리의 루트를 기준으로 왼쪽 서브 트리, 오른쪽 서브 트리를 각각 중위 순회하면 노드들의 열 번호를 알 수 있습니다.


왼쪽 서브트리를 중위 순회한 결과는 (8, 4, 2, 14, 9, 18, 15, 5, 10)
오른쪽 서브트리를 중위 순회한 결과는 (16, 11, 6, 12, 3, 17, 19, 13, 7)


그러면 벡터에 다음을 4가지를 push 하면 인덱스로 트리에서 각 정점의 열 번호를 알 수 있습니다.

 

1. 인덱스를 1부터 사용하기위해 0 push

2. 루트의 왼쪽 서브트리 중위 순회 결과 push

3. 루트 push

4. 루트의 오른쪽 서브트리 중위 순회 결과 push

 

vector = (0, 8, 4, 2, 14, 9, 18, 15, 5, 10, 1, 16, 11, 6, 12, 3, 17, 19, 13, 7)

 

그리고 이제 bfs로 레벨 순회를 해서 각 정점의 레벨을 알아낸 뒤 레벨별로 너비의 최댓값을 구하면 됩니다.

 

<C++ 소스 코드>

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
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
 
int n;
int lc[10001];
int rc[10001];
int level[10001];
bool isRoot[10001];
vector<int> col;
 
 
int getRoot() {
    for (int i = 1; i <= n; i++) {
        if (lc[i] != -1) isRoot[lc[i]] = false;
        if (rc[i] != -1) isRoot[rc[i]] = false;
    }
    for (int i = 1; i <= n; i++if (isRoot[i]) return i;
}
 
/*레벨 순회*/
void bfs(int root) {
    queue<int> q;
    q.push(root);
    level[root] = 1;
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        if (lc[cur] != -1) {
            q.push(lc[cur]);
            level[lc[cur]] = level[cur] + 1;
        };
        if (rc[cur] != -1) {
            q.push(rc[cur]);
            level[rc[cur]] = level[cur] + 1;
        }
    }
}
 
/*중위 순회*/
void inorder(int root) {
    if (lc[root] != -1) inorder(lc[root]);
    col.push_back(root);
    if (rc[root] != -1)inorder(rc[root]);
}
 
int main(void) {
    ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
 
    cin >> n;
 
    fill(lc, lc + 10001-1);
    fill(rc, rc + 10001-1);
 
    for (int i = 0; i < n; i++) {
        int v, l, r;
        cin >> v >> l >> r;
        lc[v] = l;
        rc[v] = r;
        isRoot[v] = true//처음엔 모든 정점을 root로 가정
    }
 
 
    int root = getRoot();
    bfs(root); // 레벨 순회
    col.push_back(0); // 열 인덱스를 1부터 사용하기 위함
    if (lc[root] != -1) inorder(lc[root]); // 루트의 왼쪽 노드 col 벡터에 추가
    col.push_back(root); // root 노드 col 벡터에 추가
    if (rc[root] != -1) inorder(rc[root]); // 루트의 오른쪽 노드 col 백터에 추가
 
    int size = col.size();
    int width = 0;
    int min_level = 10000;
 
    for (int s = 1; s < size; s++) {
        for (int e = size - 1; e >= s; e--) {
            if (level[col[s]] == level[col[e]]) {
                if (e - s + 1 == width) {
                    width = e - s + 1;
                    min_level = min(min_level, level[col[s]]);
                }
                else if (e - s + 1 > width) {
                    min_level = level[col[s]];
                    width = e - s + 1;
                }
                break;
            }
        }
    }
 
    cout << min_level << " " << width;
    return 0;
}
cs

 

반응형

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[백준 2623번] 음악프로그램  (0) 2020.04.11
[백준 2252번] 줄 세우기  (0) 2020.04.03
[백준 11725번] 트리의 부모 찾기  (0) 2020.03.26
[백준 1043번] 거짓말  (0) 2020.03.25
[백준 6118번] 숨바꼭질  (0) 2020.03.23
반응형

문제 링크: https://www.acmicpc.net/problem/11725

 

11725번: 트리의 부모 찾기

루트 없는 트리가 주어진다. 이때, 트리의 루트를 1이라고 정했을 때, 각 노드의 부모를 구하는 프로그램을 작성하시오.

www.acmicpc.net

<문제 풀이> 그래프, 트리, DFS, BFS

 

트리는 그래프의 특수한 형태이므로 그래프 탐색을 그대로 사용하면 됩니다. 노드 1을 시작으로 bfs를 돌리면 되는데, 각 노드의 인접한 노드들을 큐에 넣을 때는 부모를 제외한 자식 노드들만 넣으면 됩니다. 왜냐하면 큐에서 꺼낸 각 노드의 부모는 이전에 이미 방문했기 때문입니다. 

<C++ 소스 코드>

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 <queue>
#include <vector>
using namespace std;
 
vector<int>adj[100001];
int parents[100001];
int n;
 
void bfs() {
    queue<int> q;
    q.push(1);
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        for (auto& next : adj[cur]) {
            if (parents[cur] == next)continue;
            q.push(next);
            parents[next] = cur;
        }
    }
}
 
int main(void) {
    ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
    
    cin >> n;
    
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
 
    bfs();
 
    for (int i = 2; i <= n; i++) {
        cout << parents[i] << '\n';
    }
 
    return 0;
}
cs

 

반응형

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[백준 2252번] 줄 세우기  (0) 2020.04.03
[백준 2250번] 트리의 높이와 너비  (0) 2020.03.30
[백준 1043번] 거짓말  (0) 2020.03.25
[백준 6118번] 숨바꼭질  (0) 2020.03.23
[백준 5567번] 결혼식  (0) 2020.03.22
반응형

문제 링크: https://www.acmicpc.net/problem/1043

 

1043번: 거짓말

지민이는 파티에 가서 이야기 하는 것을 좋아한다. 파티에 갈 때마다, 지민이는 지민이가 가장 좋아하는 이야기를 한다. 지민이는 그 이야기를 말할 때, 있는 그대로 진실로 말하거나 엄청나게 과장해서 말한다. 당연히 과장해서 이야기하는 것이 훨씬 더 재미있기 때문에, 되도록이면 과장해서 이야기하려고 한다. 하지만, 지민이는 거짓말쟁이로 알려지기는 싫어한다. 문제는 몇몇 사람들은 그 이야기의 진실을 안다는 것이다. 따라서 이런 사람들이 파티에 왔을 때는, 지민

www.acmicpc.net

<문제 풀이> 그래프, Union-Find (유니온- 파인드)

이 문제는 Union-Find 알고리즘을 이용하면 쉽게 풀 수 있습니다.

먼저 문제에 나온 테스트 케이스를 예시로 보면

4 3

0

2 1 2

1 3

3 2 3 4

 

위 그림에서 각 파티에 어떤 한 사람이라도 진실을 아는 사람이면 곧 그 파티에 존재하는 모든 사람은 진실을 아는 사람이 됩니다. 예제에서 진실을 아는 사람의 수가 0이므로 과장된 이야기를 할 수 있는 파티의 개수는 3이 됩니다.

 

만약에 진실을 아는 사람이 2번이라고 해보겠습니다. 그러면 입력은 다음과 같이 주어집니다.

 

4 3

1 2

2 1 2

1 3

3 2 3 4

그러면 처음에 2번 사람이 진실을 아는 사람이니깐 2번 사람으로부터 바이러스가 퍼지듯이 연결된 모든 사람이 진실을 아는 사람이 됩니다.

 

이때 답은 0이 됩니다.

 

그러면 결국 각 파티마다 입력되는 사람들을 같은 그래프로 묶고, 진실을 아는 사람과 같은 그래프에 있는지만 확인하면 됩니다.

 

같은 그래프로 묶는건 Union, 같은 그래프에 속해 있는지 판별하는 건 Find을 이용하면 됩니다.

 

 

 

<C++ 소스 코드>

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
#include <iostream>
#include <vector>
using namespace std;
 
int n, m, k;
int parents[51];
vector<int> know;
vector<vector<int> > v(50);
 
 
int Find(int x) {
    if (parents[x] == x) return x;
    return x = Find(parents[x]);
}
 
void Union(int x, int y) {
    x = Find(x);
    y = Find(y);
    parents[x] = y;
}
 
 
 
int main(void) {
    ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
    
    cin >> n >> m >> k;
 
    while (k--) {
        int t;
        cin >> t;
        know.push_back(t);
    }
 
    for (int i = 1; i <= n; i++) parents[i] = i;
 
    for (int i = 0; i < m; i++) {
        int p;
        cin >> p;
        int num;
        int prev;
        for (int j = 0; j < p; j++) {
            if (j >= 1) {
                prev = num;
                cin >> num;
                Union(prev, num);
            }
            else {
                cin >> num;
            }
            v[i].push_back(num);
        }
    }
    for (auto& list : v) {
        bool flag = false;
        for (auto& person : list) {
            if (flag) break;
            for (auto& t : know) {
                if (Find(person) == Find(t)) {
                    flag = true;
                    break;
                }
            }
        }
        if (flag)m--;
    }
    cout << m;
 
    return 0;
}
cs

 

반응형

+ Recent posts