[グラフ理論-講義8]最小全域木問題のためのクラスカルアルゴリズム

ฅ(๑˙o˙๑)ฅみなさん、こんにちは、私のブログへようこそ:Ania学習アルゴリズムの
一連の学習ノートは継続的に更新されています〜

A



I.はじめに

最小スパニングツリーの定義:

n個のノードを持つ連結グラフのスパニングツリーは、元のグラフの最小の連結サブグラフであり、元のグラフのn個のノードすべてを含み、グラフの連結を維持するエッジが最も少なくなります。最小全域木は、クラスカルアルゴリズムまたはプリムアルゴリズムによって計算できます。

最小スパニングツリーは実際に最小权重生成树


次に、最小全域木を見つけるためのクラスカルアルゴリズム

時間計算量O(mlogm) -mはエッジの数であり、スパースエッジを持つネットワークの最小スパニングツリーを見つけるのに適しています

クラスカルのアルゴリズムは、貪欲という考えに基づいています。

クラスカルアルゴリズムの簡単な説明

まず、重みに応じてすべてのエッジを小さいものから大きいものに配置し、次に各エッジを順番に選択します。このエッジの2つの端点が同じセットに属していない場合は、すべての点が同じになるまでそれらをマージします。セットまで。コレクションまで。

実装手順

  • すべてのエッジをウェイトのサイズに従って昇順で並べ替えてから、小さいものから大きいものへと1つずつ判断します。

  • このエッジが以前に選択したすべてのエッジと回路を形成しない場合は、このエッジを選択して分割します。それ以外の場合は、破棄します。

  • n個の頂点を持つ接続されたネットワークがn-1個のエッジをフィルターで除去するまで。

  • 選択したエッジとすべての頂点は、この接続されたネットワークの最小スパニングツリーを構成します。

ループがあるかどうかを判断する方法は次のとおりです。ユニオン検索を使用します。

各頂点には、初期状態で異なるセットが与えられます。
プロセスの各エッジをトラバースして、2つの頂点がセットに含まれているかどうかを判断します。
エッジ上の2つの頂点がセットに含まれている場合、それは2つの頂点が接続されていることを意味し、このエッジは必要ありません。セットにない場合は、このエッジが必要です。

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;
}

やっと

莫言、真実は無限大です、進歩のあらゆるインチはそれ自身を持っています

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/m0_63233163/article/details/125608760