본문 바로가기

Study/BOJ

BOJ - 행성 터널(2887)

처음에 이 문제를 접했을 때, 사실 왜 gold 1이나 부여되어 있는지 몰랐는데 그대로 제출하니 메모리 초과가 발생하였다. 

 

N이 10만개가 들어왔을 경우 내 알고리즘은 현재 N * (N-1)개 만큼의 간선정보를 저장하고 있다. 하지만, 이 문제는 모든 간선에 대한 정보를 알 필요 없이 x축, y축, z축에 대해 인접한 행성만 보면 된다. 그 이유는 백준 질문게시판에 나와있는 아래 내용과 같다.

주어진 그래프의 MST를 T라고 합시다. 

인접하지 않은 정점 A, B가 T에서 간선으로 연결되어있다고 가정하죠.

일반성을 잃지 않고 A, B 사이에 연결된 간선 무게가 |A_x - B_x| 라고 하죠. x 축으로 A, B 사이에 있는 정점 C가 존재합니다 (A_x <= C_x <= B_x)

여기서 A, B를 연결하는 간선을 제거하고 A, C 와 C, B를 연결합니다 (이 그래프를 G라고 하죠) G는 N개의 간선으로 이루어져 있고, 연결되어있다는건 자명합니다.

그리고 총 무게 변화는 최대 -|A_x - B_x| + |A_x - C_x| + |C_x - B_x| = 0 입니다. 즉 G의 싸이클에서 간선 아무거나 지워도 T보다 총 무게가 더 작습니다.

T가 MST라는 가정이 모순되니 MST에서 연결된 정점은 인접합니다.

코드 

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
#include<iostream>
#include<vector>
#include<algorithm>
 
using namespace std;
 
int N;
typedef long long ll;
typedef struct coor
{
    ll x,y,z;
    int idx;
}coor;
 
typedef struct graph
{
    int v1, v2;
    ll cost;
}graph;
 
vector<coor> v;
vector<graph> g;
 
int getRoot(vector<int> &parent, int idx)
{
    if (parent[idx] == idx) return idx;
    else return parent[idx] = getRoot(parent, parent[idx]);
}
 
void merge(vector<int> &parent, int a, int b)
{
    a = getRoot(parent, a); b = getRoot(parent, b);
    if (a == b) return;
    else if (a < b) parent[b] = a;
    else parent[a] = b;
}
 
bool comp(graph a, graph b)
{
    return a.cost < b.cost;
}
 
bool compX(coor a, coor b)
{
    return a.x < b.x;
}
 
bool compY(coor a, coor b)
{
    return a.y < b.y;
}
 
bool compZ(coor a, coor b)
{
    return a.z < b.z;
}
 
int main()
{
    cin.tie(NULL);
    ios_base::sync_with_stdio(false);
    cin >> N;
    for(int i=0; i<N; i++)
    {
        coor c;
        cin >> c.x >> c.y >> c.z;
        c.idx = i + 1;
        v.push_back(c);
    }
    sort(v.begin(), v.end(), compX);
    for(int i=1; i<N; i++)
    {
        long long dist = min(min(abs(v[i-1].x - v[i].x), abs(v[i-1].y - v[i].y)), abs(v[i-1].z - v[i].z));
        graph tmp;
        tmp.v1 = v[i-1].idx;
        tmp.v2 = v[i].idx;
        tmp.cost = dist;
        g.push_back(tmp);
    }
    sort(v.begin(), v.end(), compY);
    for(int i=1; i<N; i++)
    {
        long long dist = min(min(abs(v[i-1].x - v[i].x), abs(v[i-1].y - v[i].y)), abs(v[i-1].z - v[i].z));
        graph tmp;
        tmp.v1 = v[i-1].idx;
        tmp.v2 = v[i].idx;
        tmp.cost = dist;
        g.push_back(tmp);
    }
    sort(v.begin(), v.end(), compZ);
    for(int i=1; i<N; i++)
    {
        long long dist = min(min(abs(v[i-1].x - v[i].x), abs(v[i-1].y - v[i].y)), abs(v[i-1].z - v[i].z));
        graph tmp;
        tmp.v1 = v[i-1].idx;
        tmp.v2 = v[i].idx;
        tmp.cost = dist;
        g.push_back(tmp);
    }
    sort(g.begin(), g.end(), comp);
    long long sum = 0;
    vector<int> parent(N + 1);
    for(int i=1; i<=N; i++)
        parent[i] = i;
    for(int i=0; i<g.size(); i++)
    {
        int v1 = g[i].v1; int v2 = g[i].v2;
        if (getRoot(parent, v1) != getRoot(parent, v2))
        {
            merge(parent, v1, v2);
            sum += g[i].cost;
        }
    }
    cout << sum << "\n";
    return (0);
}
cs

위 코드와 같이 X, Y, Z축에 대해 각각 정렬한 뒤 인접한 행성들의 간선들을 모두 g에 저장한다. 그러면 6 * N개 만큼의 정보가 저장되어 있으므로 간선의 개수가 확연히 줄은 것을 알 수 있다. 

이에 대해 크루스칼 알고리즘을 적용하면 쉽게 답을 구할 수 있다.

'Study > BOJ' 카테고리의 다른 글

BOJ - 구슬 탈출 2(13460)  (0) 2021.04.21
BOJ - 어른 상어(19237)  (0) 2021.04.21
BOJ - 다리 만들기 2(17472)  (0) 2021.04.15
BOJ - 네트워크 연결(1922)  (0) 2021.04.15
BOJ - 거짓말(1043)  (0) 2021.04.14