[그래프 이론 - 8강] 최소 스패닝 트리 문제에 대한 크루스칼 알고리즘

ฅ(๑˙o˙๑)ฅ 안녕하세요 여러분, 제 블로그에 오신 것을 환영합니다: Ania 학습 알고리즘에 대한
학습 노트 시리즈 는 지속적으로 업데이트됩니다~

ㅏ



I. 소개

최소 스패닝 트리 정의:

n개의 노드가 있는 연결된 그래프의 스패닝 트리는 원래 그래프의 최소 연결된 하위 그래프이며 원래 그래프의 모든 n개의 노드를 포함하며 그래프를 연결하는 가장 작은 모서리를 가집니다. 최소 신장 트리는 kruskal 알고리즘 또는 prim 알고리즘으로 계산할 수 있습니다.

最小权重生成树최소 스패닝 트리는 실제로


둘째, 최소 스패닝 트리를 찾는 Kruskal 알고리즘

시간 복잡도 O(mlogm) - m은 에지의 수로, 에지가 희소한 네트워크의 최소 스패닝 트리를 찾는 데 적합합니다.

Kruskal의 알고리즘은 탐욕의 아이디어를 기반으로 합니다.

Kruskal 알고리즘에 대한 간략한 설명

먼저, 가중치에 따라 작은 것부터 큰 것까지 모든 edge를 배열한 후, 각 edge를 순서대로 선택하고, 이 edge의 두 끝점이 같은 집합에 속하지 않으면 모든 점이 같은 집합에 속할 때까지 병합한다. 컬렉션까지.

구현 단계

  • 모든 모서리를 가중치의 크기에 따라 오름차순으로 정렬한 다음 작은 것부터 큰 것까지 하나씩 판단합니다.

  • 이 모서리가 이전에 선택한 모든 모서리로 회로를 형성하지 않는 경우 이 모서리를 선택하여 분할하고, 그렇지 않으면 폐기합니다.

  • n개의 정점이 있는 연결된 네트워크가 n-1개의 가장자리를 걸러낼 때까지.

  • 선택된 에지와 모든 정점은 이 연결된 네트워크의 최소 스패닝 트리를 구성합니다.

루프가 있는지 여부를 판단하는 방법은 다음과 같습니다. use union search .

각 꼭짓점에는 초기 상태에서 다른 집합이 지정됩니다.
프로세스의 각 모서리를 탐색하여 두 정점이 집합에 있는지 확인합니다.
가장자리에 있는 두 정점이 집합에 있으면 두 정점이 연결되어 있고 이 가장자리가 필요하지 않습니다. 세트에 없으면 이 모서리가 필요합니다.

int Kruskal()
{
    
    
    int res=0,cnt=0;//res记录最小生成树的树边权重之和,cnt记录的是全部加入到树的集合中边的数量(可能有多个集合)
    for(int i=0;i<m;i++)
    {
    
    
        int a=edges[i].a,b=edges[i].b,w=edges[i].w;
        if(find(a)!=find(b))
        //如果 a b 不在一个集合中
        {
    
    
            p[find(a)]=p[find(b)];//将a,b所在的两个集合连接起来
            cnt++;//因为加入的是a-b的这一条边,将a,b所在的两个集合连接之后,全部集合中的边数加1
            res+=w;//加入到集合中的边的权重之和
        }
    }

    if(cnt==n-1) return res;//树中有n个节点便有n-1条边,可以生成最小生成树
    else return 0x3f3f3f3f;//如果cnt不等于n-1的话,说明无法生成有n个节点的树
}

예시:

n개의 점과 m개의 간선이 있는 무방향 그래프가 주어지면 그래프에 다중 간선과 자체 루프가 있을 수 있으며 간선 가중치는 음수일 수 있습니다.
최소 신장 트리의 트리 간선 가중치의 합을 구하고, 최소 신장 트리가 없으면 출력이 불가능합니다.

입력 샘플:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
출력 샘플:
6

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=200010,M=100010;         

int p[M];
int n,m;

struct Edge
{
    
    
    int a,b,w;
//重载小于号,因为在给边排序的时候是按照边的权重进行排序的,这样当两个边进行比较的时候就会使用它们的权重进行比较了
     bool operator< (const Edge &W)const
    {
    
    
        return w < W.w;
    }
}edges[N];

int find(int x)
{
    
    
    if(p[x]!=x) p[x]=find(p[x]);
    else return x;
} 

int Kruskal()
{
    
    
    int res=0,cnt=0;//res记录最小生成树的树边权重之和,cnt记录的是全部加入到树的集合中边的数量(可能有多个集合)
    for(int i=0;i<m;i++)
    {
    
    
        int a=edges[i].a,b=edges[i].b,w=edges[i].w;
        if(find(a)!=find(b))
        {
    
    
            p[find(a)]=p[find(b)];//将a,b所在的两个集合连接起来
            cnt++;//因为加入的是a-b的这一条边,将a,b所在的两个集合连接之后,全部集合中的边数加1
            res+=w;//加入到集合中的边的权重之和
        }
    }

    if(cnt==n-1) return res;//可以生成最小生成树
    else return 0x3f3f3f3f;//树中有n个节点便有n-1条边,如果cnt不等于n-1的话,说明无法生成有n个节点的树
}

int main()
{
    
    
    cin>>n>>m;

    for(int i=0;i<n;i++) p[i]=i;//初始化并查集

    for(int i=0;i<m;i++)//读入每条边
    {
    
    
        int a,b,w;
        cin>>a>>b>>w;
        edges[i]={
    
    a,b,w};
    }

    sort(edges,edges+m);//将边的权重按照大小一一排序

    int t=Kruskal();

    if(t==0x3f3f3f3f) puts("impossible\n");
    else cout<<t<<endl;

    return 0;
}

마침내

Mo Yan, 진실은 끝이 없어, 진보의 모든 부분에는 고유한 것이 있습니다

여기에 이미지 설명 삽입

추천

출처blog.csdn.net/m0_63233163/article/details/125608760