반응형

 

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

 

1761번: 정점들의 거리

첫째 줄에 노드의 개수 N이 입력되고 다음 N-1개의 줄에 트리 상에 연결된 두 점과 거리를 입력받는다. 그 다음 줄에 M이 주어지고, 다음 M개의 줄에 거리를 알고 싶은 노드 쌍이 한 줄에 한 쌍씩

www.acmicpc.net

 

 

[ 문제풀이 ]

 

문제 풀이 과정은 다음과 같습니다.

 

1. 각각의 노드들에 대해서 연결된 에지들을 list배열에 저장합니다.

 

2. 1번 노드를 루트 노드로 설정하고, 부모는 0, 깊이는 1로 설정합니다. 

 

3. 각 노드들의 부모, 깊이, 부모 노드까지의 거리를 각각 저장합니다.

 

4. 입력받은 노드들에 대해서 높이가 같아질 때까지 올라가고 그때까지의 거리를 저장합니다.

 

5. 노드들의 깊이가 같아졌을 때 같은 노드를 가리키고 있다면 break

 

6. 다른 노드를 가리키고 있다면 계속 올라가면서 5번을 반복합니다.

 

하지만 같은 깊이까지 한칸씩 올라가면 드는 시간이 최대 O(NM)이 걸리게 되므로 효율적이지 않습니다.

 

따라서, 깊이 정보를 $2^{i}$로 저장하게 된다면 O(MlogN)의 시간으로 단축할 수 있습니다.

 

첫 번째 코드는 같은 깊이까지 한 칸씩 올라가는 코드이고, 두 번째 코드는 $2^{i}$칸씩 올라가는 코드입니다.

 

[ 소스 코드 ]

 

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
#include<iostream>        //768ms
#include<vector>
#include<queue>
 
using namespace std;
 
struct node {
    int to;
    int dist;
};
 
struct tree {
    int parent;
    int depth;
    int dist;
};
 
int N, M;
vector<node> list[40001];        //연결된 노드 저장
int visited[40001];
tree arr[40001];            //부모 노드, 깊이, 거리를 저장할 배열
 
int main()
{
    cin >> N;
 
    arr[1= { 010 };
 
    for (int i = 0; i < N - 1; i++) {        //연결된 노드 저장
        int a, b, dist;
        scanf("%d %d %d"&a, &b, &dist);
        list[a].push_back({ b,dist });
        list[b].push_back({ a,dist });
    }
 
    queue<node> q;
    q.push({ 10 });
 
    while (!q.empty())        //1번 노드의 깊이를 1로 두고 내려갈수록 깊이 +1
    {
        int to = q.front().to;
        q.pop();
 
        if (visited[to] == 1continue;
        visited[to] = 1;
 
        for (int i = 0; i < list[to].size(); i++) {
            int next = list[to][i].to;
            int dist = list[to][i].dist;
            if (visited[next] != 1) {
                arr[next] = { to, arr[to].depth + 1, dist };
                q.push({ next, 0 });
            }
        }
    }
 
    cin >> M;
 
    for (int i = 0; i < M; i++) {
        int a, b;
        int ans = 0;
        scanf("%d %d"&a, &b);
        if (arr[a].depth < arr[b].depth) {            //깊이가 다르다면 깊이 맞춰주기
            while (arr[a].depth != arr[b].depth) {
                ans += arr[b].dist;
                b = arr[b].parent;                
            }
        }
        else if (arr[a].depth > arr[b].depth) {
            while (arr[a].depth != arr[b].depth) {
                ans += arr[a].dist;
                a = arr[a].parent;
            }
        }
        //깊이가 같다면
        while (1) {        //부모가 같아질 때까지 올라가기
            if (a == b) {
                break;
            }
            ans += arr[a].dist + arr[b].dist;
            a = arr[a].parent;
            b = arr[b].parent;
        }
 
        printf("%d\n", ans);
    }
}
cs

 

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
#include<iostream>            //40ms
#include<vector>
 
using namespace std;
 
struct node {
    int to;
    int dist;
};
 
int N, M;
vector<node> list[40001];        //연결된 노드 저장
int visited[40001];
int parent[40001][16];        //parent[i][j] i번 노드의 2^i번째 부모
int dist[40001][16];        //dist[i][j] i번 노드의 2^i번째 부모까지의 거리
int depth[40001];            //노드의 깊이 배열
 
void dfs(int cur, int D)    //현재 노드와 깊이
{
    if (visited[cur]) return;
    visited[cur] = 1;
    depth[cur] = D;
 
    for (int i = 0; i < list[cur].size(); i++) {
        int next = list[cur][i].to;
        int cost = list[cur][i].dist;
        if (visited[next] == 1continue;
        parent[next][0= cur;
        dist[next][0= cost;
        dfs(next, D + 1);
    }
}
 
int LCA(int start, int end)        //공통 조상 찾기
{
    if (depth[start] > depth[end]) {
        int temp = start;
        start = end;
        end = temp;
    }
 
    int ret = 0;
 
    for (int i = 15; i >= 0; i--)    //깊이를 같게 만들기
    {
        int mask = 1 << i;
 
        if (depth[end- depth[start] >= mask)
        {
            ret += dist[end][i];
            end = parent[end][i];
        }
    }
 
    if (start == end) {    //깊이가 같을 때 같은 노드에 있다면
        return ret;
    }
 
    for (int i = 15; i >= 0; i--) {        //부모가 같다면 최초의 공통 조상이거나 넘었거나
        if (parent[start][i] == parent[end][i]) continue;
 
        ret += dist[start][i] + dist[end][i];
        start = parent[start][i];
        end = parent[end][i];
    }
 
    ret += dist[start][0+ dist[end][0];    //start, end는 부모가 같으므로 한 칸 더 올라가야함
    return ret;
}
 
int main()
{
    cin >> N;
 
    for (int i = 0; i < N - 1; i++) {        //연결된 노드 저장
        int a, b, dist;
        scanf("%d %d %d"&a, &b, &dist);
        list[a].push_back({ b,dist });
        list[b].push_back({ a,dist });
    }
 
    dfs(11);
 
    for (int i = 1; i < 16; i++) {        //parent[node][i] node의 2^i번째 부모
        for (int j = 1; j <= N; j++) {
            int preParent = parent[j][i - 1];
            parent[j][i] = parent[preParent][i - 1];
 
            if (parent[j][i] == 0continue;
 
            int preDist = dist[j][i - 1];
            dist[j][i] = preDist + dist[preParent][i - 1];
        }
    }
 
    cin >> M;
 
    for (int i = 0; i < M; i++) {
        int a, b;
        scanf("%d %d"&a, &b);
        printf("%d\n", LCA(a, b));
    }
}
cs
반응형
반응형

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

 

5639번: 이진 검색 트리

트리를 전위 순회한 결과가 주어진다. 노드에 들어있는 키의 값은 106보다 작은 양의 정수이다. 모든 값은 한 줄에 하나씩 주어지며, 노드의 수는 10,000개 이하이다. 같은 키를 가지는 노드는 없다

www.acmicpc.net

 

 

[ 문제풀이 ]

 

입력받은 수의 부모 노드를 기준으로 왼쪽에 있는 노드와 오른쪽에 있는 노드를 구분하여 왼쪽 노드부터 출력해주면 됩니다.

 

현재 노드에서 값이 커지는 부분이 오른쪽 노드이므로 for문을 돌려서 현재 노드 +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
#include<iostream>
 
using namespace std;
 
int arr[10000];
int i;
 
void dfs(int node, int size)
{
    if (node >= sizereturn;
 
    for (i = node + 1; i < size; i++) {
        if (arr[node] < arr[i]) break;
    }
 
    dfs(node + 1, i);    //왼쪽
    dfs(i, size);        //오른쪽
    printf("%d\n", arr[node]);
 
    return;
}
 
int main()
{
    int idx = 0;
 
    while (scanf("%d"&arr[idx]) != EOF)
    {
        idx++;
    }
 
    dfs(0, idx);
}
cs
반응형
반응형

 

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

 

14725번: 개미굴

첫 번째 줄은 로봇 개미가 각 층을 따라 내려오면서 알게 된 먹이의 정보 개수 N개가 주어진다.  (1 ≤ N ≤ 1000) 두 번째 줄부터 N+1 번째 줄까지, 각 줄의 시작은 로봇 개미 한마리가 보내준 먹이

www.acmicpc.net

 

 

[ 문제풀이 ]

 

트라이의 개념을 모른다면 공부하고 이 문제를 푸는 것을 추천합니다!

 

문제의 조건에서 사전 순으로 출력하라고 했으므로 map 자료구조를 이용해서 문제를 풀었습니다. 각각의 노드들은 자식 노드를 가지고 있고, 자식 노드들의 키값은 string입니다. 따라서 map <string, Trie*> map을 만들어 주고 이 노드는 자식 노드의 정보를 포인터의 형식으로 가지고 있습니다.

 

먹이를 벡터로 입력 받아서 만약 해당 깊이에 단어가 존재한다면 그 단어의 자식 노드로 넘어가고, 없다면 단어를 동적 할당을 통해 생성해줍니다. 그러고 나서 자식 노드로 넘어가게 됩니다.

 

이러한 방식으로 모든 입력을 처리하면 map의 특성상 자동으로 오름차순으로 정렬이 되기때문에 따로 정렬해줄 필요 없이 Find 함수를 통해서 출력해주면 됩니다.

 

Find 함수 또한 iiterator를 차례로 순회해주면서 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
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<iostream>
#include<string>
#include<map>
#include<vector>
 
using namespace std;
 
int N;
 
struct Trie {
    map<string, Trie*> m;        //오름차순으로 출력하기 위해서 map을 씀
    ~Trie() {                    //소멸자로 동적 할당한 Trie들을 delete
        for (auto it = m.begin(); it != m.end(); it++) {
            delete it->second;
        }
    }
 
    void Insert(vector<string> vect, int idx)
    {
        if (vect.size() == idx) return;    //모두 삽입했다면 return
 
        if (m.find(vect[idx]) == m.end()) {    //vect[idx]가 map에 없다면
            m.insert({ vect[idx], new Trie });
        }
 
        m[vect[idx]]->Insert(vect, idx + 1);    //다음 단어를 삽입
    }
 
    void Find(int depth)
    {
        for (auto it = m.begin(); it != m.end(); it++) {
            for (int i = 0; i < depth; i++) {
                printf("--");
            }
            cout << it->first << '\n';
            
            it->second->Find(depth + 1);
        }
    }
};
 
int main()
{
    Trie *root = new Trie();
    scanf("%d"&N);
 
    for (int i = 0; i < N; i++) {
        int n;
        scanf("%d"&n);
        vector<string> vect(n);
        for (int j = 0; j < n; j++) {
            cin >> vect[j];
        }
 
        root->Insert(vect, 0);
    }
 
    root->Find(0);
    delete root;
}
cs
반응형
반응형

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

 

2533번: 사회망 서비스(SNS)

첫 번째 줄에는 친구 관계 트리의 정점 개수 N이 주어진다. 단, 2 ≤ N ≤ 1,000,000이며, 각 정점은 1부터 N까지 일련번호로 표현된다. 두 번째 줄부터 N-1개의 줄에는 각 줄마다 친구 관계 트리의 에

www.acmicpc.net

 

 

[ 문제풀이 ]

 

이 문제의 조건은 사이클이 없는 트리이기 때문에 주어지는 양방향 그래프를 단방향 트리로 먼저 바꾸어야 합니다. 그 후 dfs와 dp를 활용하여 문제를 풀어주면 됩니다.

 

먼저 각 노드별로 상태가 2가지(얼리어답터이거나 아니거나)씩 있습니다. 이를 활용하여 만약 현재 노드가 얼리어답터라면 다음 노드가 얼리어답터이든 아니든 큰 상관이 없습니다. 하지만 현재 노드가 얼리어답터가 아니라면 다음 노드는 무조건 얼리어답터이어야 합니다.

 

위의 조건을 이용하여 모든 노드들을 체크하면 최종 min(dfs(1,0), dfs(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
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
#include<iostream>
#include<vector>
#include<cstring>
 
using namespace std;
 
vector<int> list[1000001];
vector<int> tree[1000001];
int dp[1000001][2];
int visited[1000001];
int N;
int ans = 987654321;
 
int dfs(int node, int state)        //상태에 따라 최솟값을 저장
{
    if (dp[node][state] != -1return dp[node][state];
    dp[node][state] = state;
 
    if (state == 0) {
        for (int i = 0; i < tree[node].size(); i++) {
            int next = tree[node][i];
            dp[node][state] += dfs(next, 1);
        }
    }
    else {
        for (int i = 0; i < tree[node].size(); i++) {
            int next = tree[node][i];
            dp[node][state] += min(dfs(next, 0), dfs(next, 1));
        }
    }
 
    return dp[node][state];
}
 
void makeTree(int node)        //그래프를 트리로 재설정
{
    visited[node] = 1;
 
    for (int i = 0; i < list[node].size(); i++) {
        int next = list[node][i];
        if (visited[next] != 1) {
            tree[node].push_back(next);
            makeTree(next);
        }
    }
}
 
int main()
{
    cin >> N;
 
    for (int i = 0; i < N - 1; i++) {
        int u, v;
        scanf("%d %d"&u, &v);
        list[u].push_back(v);
        list[v].push_back(u);
    }
 
    makeTree(1);
 
    memset(dp, -1sizeof(dp));
 
    int ans = min(dfs(10), dfs(11));
 
    cout << ans;
}
cs
반응형
반응형

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

 

1167번: 트리의 지름

트리가 입력으로 주어진다. 먼저 첫 번째 줄에서는 트리의 정점의 개수 V가 주어지고 (2 ≤ V ≤ 100,000)둘째 줄부터 V개의 줄에 걸쳐 간선의 정보가 다음과 같이 주어진다. 정점 번호는 1부터 V까지

www.acmicpc.net

 

 

[ 문제풀이 ]

 

풀이 방법을 생각해내는데 시간이 조금 걸렸지만 방법만 알아낸다면 매우 쉬운 문제였습니다.

 

먼저 지름은 어떤 점에서 출발하던 지 가장 먼 곳에 있습니다. 이를 이용하여 먼저 한 점에서 가장 먼 점을 찾고, 그 점으로부터 가장 먼 점을 찾으면 지름을 구할 수 있습니다.

 

[ 소스 코드 ]

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
#include <iostream>
#include <vector>
#include <cstring>
 
using namespace std;
 
struct node {
    int to;
    int dist;
};
 
int V;
vector<node> list[100001];
int visited[100001];
int startNode;
int ans;
 
void dfs(int num, int dist)            //시작점으로부터 가장 먼 노드와 거리를 저장
{
    if (visited[num] == 1) {
        return;
    }
    visited[num] = 1;
 
    if (ans < dist) {
        ans = dist;
        startNode = num;
    }
 
    for (int i = 0; i < list[num].size(); i++) {
        int next = list[num][i].to;
        int nextDist = list[num][i].dist;
        dfs( next, dist + nextDist );
    }
}
 
int main()
{
    cin >> V;
 
    for (int i = 0; i < V; i++) {
        int n;
        cin >> n;
        while (1) {
            int to;
            scanf("%d"&to);
            if (to != -1) {
                int dist;
                scanf("%d"&dist);
                list[n].push_back({ to,dist });
            }
            else {
                break;
            }
        }
    }
 
    dfs(10);        //1번 노드에서 가장 먼 노드 저장
    memset(visited, 0sizeof(visited));
    dfs(startNode, 0);    //시작 노드부터 가장 먼 노드와 거리 저장
 
    cout << ans;
}
cs
반응형
반응형

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

 

1967번: 트리의 지름

파일의 첫 번째 줄은 노드의 개수 n(1 ≤ n ≤ 10,000)이다. 둘째 줄부터 n-1개의 줄에 각 간선에 대한 정보가 들어온다. 간선에 대한 정보는 세 개의 정수로 이루어져 있다. 첫 번째 정수는 간선이 연

www.acmicpc.net

 

 

[ 문제풀이 ]

 

풀이 방법을 생각해내는데 시간이 조금 걸렸지만 방법만 알아낸다면 매우 쉬운 문제였습니다.

 

먼저 지름은 어떤 점에서 출발하던 지 가장 먼 곳에 있습니다. 이를 이용하여 먼저 한 점에서 가장 먼 점을 찾고, 그 점으로부터 가장 먼 점을 찾으면 지름을 구할 수 있습니다.

 

[ 소스 코드 ]

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
#include<iostream>
#include<vector>
#include<cstring>
 
using namespace std;
 
struct Node {
    int to;
    int dist;
};
 
int n;
 
vector<Node> list[10001];
int visited[10001];
int snode;
int diameter;
 
void dfs(int num, int dist)
{
    if (visited[num] == 1) {
        return;
    }
 
    if (diameter < dist) {
        diameter = dist;
        snode = num;
    }
 
    visited[num] = 1;
 
    for (int i = 0; i < list[num].size(); i++) {
        int next = list[num][i].to;
        int nextDist = list[num][i].dist;
        dfs(next, dist + nextDist);
    }
}
 
int main()
{
    cin >> n;
 
    for (int i = 0; i < n-1; i++) {
        int p, c, d;
        scanf("%d %d %d"&p, &c, &d);
        list[p].push_back({ c,d });
        list[c].push_back({ p,d });
    }
 
    dfs(10);        //한 노드에서 가장 먼 노드를 찾기
 
    memset(visited, 0sizeof(visited));
 
    dfs(snode, 0);    //위에서 찾은 노드에서 가장 먼 노드를 찾기
 
    cout << diameter;
}
cs
반응형

+ Recent posts