코딩/백준 문제 풀이

BOJ 2463 - 비용

stonejjun 2020. 5. 20. 22:53

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

문제 소개

그래프가 주어진다. 이 문제에서 Cost(u,v)는 가장 적은 비용의 간선부터 차례대로 끊어나갈 때, 점 u와 점 v가 처음으로 다른 컴포넌트가 될 때 까지 끊은 간선의 가중치의 총합이다. 이때 주어진 그래프에서 임의의 i<j인 (i,j)쌍에 대해 Cost(i,j)의 합을 출력하는 것이 목표이다.

문제 풀이

 이것은 굉장히 중요한 부분이다. 무엇인가를 분리하거나 끊는 문제가 나오게 되면 그 문제가 오프라인인지부터 확인해라. 여기서 오프라인이라함은 그 요구하는 것들이 여러개가 있을 때 그 처리 순서를 바꿔도 아무런 문제가 없는 것을 보통 의미한다. 일단 우리는 분리하는 것보다 합치는 것이 편하다. 특히 union & find 를 이용하면 굉장히 빠르게 컴퍼넌트들을 합칠 수 있다. 또한 오프라인이라는 점을 이용하면 거꾸로 문제를 해결해도 상관이 없다. 조건의 반대로 간선을 추가해가면서 합치는 기법이고, 생각보다 몇몇문제에 유용하게 쓰일 테크닉이다.

 가장 가중치가 큰 간선부터 그래프에 추가해주자. 이때 어떤 두 점 i와 j가 처음으로 연결이 되었다면 Cost(i,j)는 마지막으로 추가한 간선의 가중치 + 아직 그래프에 추가하지 않은 가중치의 총 합이 될 것이다.  따라서 각 간선을 추가할 때마다 몇 개의 쌍이 처음으로 연결이 되었는지를 알 수 있으면 문제를 해결할 수 있고, 이는 union & find에서 size를 같이 관리해줌으로서 해결할 수 있다. 총 시간복잡도는 $O(NlgN)$union & find에 대해서 알고 싶다면 이 글을 보자!

코드

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
#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
struct edg{
    int a,b,c;
};
bool sf(edg a, edg b)
{
    return a.c<b.c;
}
edg arr[101010];
int par[101010];
int num[101010];
int uf(int a)
{
    if(par[a]==a)
        return a;
    if(par[a]==0)
    {
        par[a]=a;
        return a;
    }
    par[a]=uf(par[a]);
    return par[a];
}
bool mergee(int a,int b)
{
    a=uf(a);
    b=uf(b);
    if(a==b) return false;
    if(num[a]>num[b])
    {
        par[b]=a;
        num[a]+=num[b];
    }
    else if(num[a]<num[b])
    {
        par[a]=b;
        num[b]+=num[a];
    }
    if(num[a]==num[b])
    {
        par[a]=b;
        num[b]+=num[a];
    }
    return true;
}
int main()
{
    int n,m,i,j,k,l;
    long long int sum=0, ans=0;
    scanf("%d %d",&n,&m);
    for(i=1;i<=m;i++)
    {
        scanf("%d %d %d",&arr[i].a,&arr[i].b,&arr[i].c);
        sum+=arr[i].c;
    }
    for(i=1;i<=n;i++)
    {
        num[i]=1;
        par[i]=i;
    }
    sort(arr+1,arr+1+m,sf);
    for(i=m;i>=1;i--)
    {
        int a,b,c,aa,bb;
        a=arr[i].a;
        b=arr[i].b;
        c=arr[i].c;
        aa=uf(a);
        bb=uf(b);
        if(aa!=bb)
        {
            ans+=sum*num[aa]*num[bb];
            mergee(a,b);
        }
        sum-=c;
    }
    printf("%lld",ans%1000000000);
}
 
cs

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

BOJ 17411 - 가장 긴 증가하는 부분 수열 6  (3) 2020.05.22
BOJ 17408 - 수열과 쿼리 24  (0) 2020.05.22
BOJ 10067 - 수열 나누기  (0) 2020.04.16
BOJ 7578 공장  (0) 2020.04.14
BOJ 2357 - 최솟값과 최댓값  (0) 2020.04.14