并查集&最小生成树

基本思想

并查集是一种维护集合的数据结构,即为 Union(合并)Find(查找)Set(集合)① 合并:合并两个集合。② 查找:判断两个元素是否在一个集合。

如图题目为例:

代码如下:

最小生成树

克鲁斯卡尔算法

一般先选取一点,不断寻找此点旁权数最小的边并连接至对应点,不断重复直至无边。当网的顶点个数较多、而边的条数较少时,使用克鲁斯卡尔算法构造最小生成树效果较好  

输入样例
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例
6  

自写代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N = 100010, M = 200010, INF = 0x3f3f3f3f;
int n,m;
int p[N];
//排序结构体存边
struct Edge
{
    int a,b,w;
    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }
}edges[M];

//并查集寻根节点
int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
//排序
    sort(edges, edges + m);
    //初始化
    for(int i = 1; i <= n; i++) p[i] = i;
    //res存所有边的总长,cnt记边
    int res = 0, cnt = 0;
    for(int i = 0; i < m; i++)
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        a = find(a), b = find(b);
        //a的根节点不等于b的根节点,说明未加入到连通块
        if(a!=b)
        {
            p[a] = b;
            res += w;
            cnt ++;
        }
    }
    if(cnt < n-1) return INF;
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);

    //存边
    for(int i = 0; i < m; i++)
    {
        int a,b,w;
        scanf("%d%d%d", &a, &b, &w);
        edges[i] = {a,b,w};
    }
    int t = kruskal();

    if(t == INF) puts("impossible");
    else printf("%d\n", t);

    return 0;
} 

普里姆算法

代码如下:

#include<bits/stdc++.h>
using namespace std;
int g[100][100],m,n,u,v,w;
int f[100],q[1000],mi;
bool vis[100000];
void prim()
{
for(int i=1;i<=n-1;i++)
{
int mn=0x3f3f3f3f;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&mn>f[j])
{
mn=f[j];
mi=j;
}
}
vis[mi]=true;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&g[mi][j]<f[j])
{
f[j]=g[mi][j];
q[j]=mi;
}
}
}
int sum=0;
for(int i=1;i<=n;i++)
{
sum+=f[i];
}
cout<<sum<<endl;
}
int main()
{
freopen(“prim.in”,”r”,stdin);
cin>>m>>n;
memset(g,0x3f3f3f,sizeof(g));
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
g[u][v]=w;
g[v][u]=w;
}
for(int i=1;i<=n;i++)
g[i][i]=0;

for(int i=1;i<=n;i++)
{
    f[i]=g[1][i];
    q[i]=1;
}
vis[1]=true;
prim();

return 0;
}

基本方法

1).输入:一个加权连通图,其中顶点集合为V,边集合为E;

2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;

3).重复下列操作,直到Vnew = V:

a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);

b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;

4).输出:使用集合Vnew和Enew来描述所得到的最小生成树

图例说明



原始的加权连通图
顶点D被任意选为起始点。顶点ABEF通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。C, GA, B, E, FD
下一个顶点为距离DA最近的顶点。BD为9,距A为7,E为15,F为6。因此,FDA最近,因此将顶点F与相应边DF以高亮表示。C, GB, E, FA, D
算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。CB, E, GA, D, F
在当前情况下,可以在CEG间进行选择。CB为8,EB为7,GF为11。点E最近,因此将顶点E与相应边BE高亮表示。C, E, GA, D, F, B
这里,可供选择的顶点只有CGCE为5,GE为9,故选取C,并与边EC一同高亮表示。C, GA, D, F, B, E
顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EGGA, D, F, B, E, C
所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。A, D, F, B, E, C, G
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇