넘치게 채우기

[BOJ] 12912 - 트리 수정 본문

PS/BOJ

[BOJ] 12912 - 트리 수정

riveroverflow 2025. 6. 20. 22:33
728x90
반응형

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

BOJ - 트리 수정

문제 유형: 트리, 그래프, 트리의 지름

문제 난이도: Gold I

시간 제한: 2초

메모리 제한: 512MB

 

문제

N개의 정점으로 이루어진 트리 T가 있다. 트리의 각 정점은 0번부터 N-1번까지 번호가 매겨져 있다.

  • 트리에서 임의의 두 정점을 연결하는 단순 경로의 개수는 1개이다.
  • 두 정점사이의 거리는 두 정점을 연결하는 단순 경로상에 있는 간선의 가중치의 합이다.
  • 트리의 지름은 트리에 존재하는 모든 경로 중에서 가장 긴 것이다.

홍준이는 T에서 간선을 하나 제거하고, 간선을 하나 추가하려고 한다. 이때, 추가하는 간선의 가중치는 제거한 간선의 가중치와 같아야 하며, 간선을 추가한 이후에도 트리를 유지해야 한다.

이때, 홍준이가 만들 수 있는 트리 중에서 지름이 가장 큰 것을 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 트리 정점의 개수 N이 주어진다. (2 ≤ N ≤ 2,000)

둘째 줄부터 N-1개의 줄에는 트리를 이루는 간선이 주어진다. 간선은 from, to, cost와 같이 세 가지 정수로 이루어져 있으며, from과 to를 연결하는 간선의 가중치가 cost라는 뜻이다. (1 ≤ cost ≤ 1,000,000,000)

 

출력

첫째 줄에 홍준이가 만들 수 있는 트리 중에서 가장 지름이 큰 것의 지름을 출력한다.

 

풀이

모든 간선을 한 번씩 제거해보는 경우를 따져보자.

간선을 하나 제거한다면, 두 개의 트리로 분리된다. 

제거한 간선을 기반으로 각각의 트리에 접근할 수 있게 된다.

두 트리의 각각의 지름을 구하고, 없앤 간선의 지름을 더한다. 이를 모든 간선을 한 번씩 독립적으로 지우는것에 대해 고려해서 구하면 된다.

 

트리에서의 각 노드간 경로는 유일하므로, 간선을 제거하는 것을 그 간선을 부속하는 노드 u, v에서 u에서 탐색하며 v를 막는 방법을 쓰면, u쪽으로 나눠진 부분의 분할된 트리를 탐색할 수 있다.

반대도 그렇다.

 

코드

C++

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
using pll = pair<ll, ll>;

int n;

ll getDiameter(ll start, ll ban, vector<vector<pll>> &adj) {
    if (adj[start].size() == 1) return 0;

    vector<ll> dist(n, LLONG_MAX);
    dist[start] = 0;
    queue<ll> q;
    q.emplace(start);
    ll maxDist1 = 0;
    ll point1 = start;
    while (!q.empty()) {
        ll curr = q.front();
        q.pop();
        if (dist[curr] > maxDist1) {
            point1 = curr;
            maxDist1 = dist[curr];
        }
        for (const auto &[nextNode, nextCost] : adj[curr]) {
            if (nextNode == ban) continue;
            if (dist[nextNode] > dist[curr] + nextCost) {
                dist[nextNode] = dist[curr] + nextCost;
                q.emplace(nextNode);
            }
        }
    }
    vector<ll> dist2(n, LLONG_MAX);
    dist2[point1] = 0;
    q.emplace(point1);
    ll diameter = 0;
    ll point2 = point1;

    while (!q.empty()) {
        ll curr = q.front();
        q.pop();
        if (dist2[curr] > diameter) {
            point2 = curr;
            diameter = dist2[curr];
        }
        for (const auto &[nextNode, nextCost] : adj[curr]) {
            if (nextNode == ban) continue;
            if (dist2[nextNode] > dist2[curr] + nextCost) {
                dist2[nextNode] = dist2[curr] + nextCost;
                q.emplace(nextNode);
            }
        }
    }

    return diameter;
}

int main(int argc, char *argv[]) {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin >> n;
    vector<vector<pll>> adj(n);
    vector<array<ll, 3>> edges(n - 1);
    for (int i = 0; i < n - 1; i++) {
        ll u, v, w;
        cin >> u >> v >> w;
        edges[i] = {u, v, w};

        adj[u].emplace_back(v, w);
        adj[v].emplace_back(u, w);
    }

    ll ans = 0;
    for (int i = 0; i < n - 1; i++) {
        ll ban1 = edges[i][0];
        ll ban2 = edges[i][1];
        ll weight = edges[i][2];

        ll d1 = getDiameter(ban1, ban2, adj);
        ll d2 = getDiameter(ban2, ban1, adj);
        ans = max(ans, d1 + d2 + weight);
    }

    cout << ans << "\n";

    return 0;
}
728x90
반응형

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

[BOJ] 6597 - 트리 복구  (0) 2025.06.22
[BOJ] 14556 - Balance  (0) 2025.06.22
[BOJ] 1463: 1로 만들기  (0) 2025.06.19
[BOJ] 2668 - 숫자고르기  (0) 2025.06.18
[BOJ] 7662 - 이중 우선순위 큐  (0) 2025.06.17