图论讲解(3)——最小生成树(基础)

关于这个东西,有的童鞋又要开始蒙了,最小生成树是个什么鬼?!

前面我们已经说过树是什么东西了,所谓最小生成树嘛,最小嘛,那就是所有生成的树中最小的那个呗!

太别扭了对吧?

好,我们来看看官方回答。

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

prim算法

Prim算法是通过先选取一个点,然后不断加入点的一个过程。
’初始化:V’={x},E’={},x是随便一个节点;
’重复下列操作,直到V’=V:
  ´在E集合当中选择最小的边<u,v>使得u∈V’但是v∉V’;
  ´V’加入节点v,E’加入<u,v>;
  ´(V’,E’)则为所求的最小生成树。

´我们可以对该算法里面的各个步骤分别考虑:
´初始化:V’={x},E’={},x是随便一个节点;
             这一步只需要随便选取一个点即可;
´重复下列操作,直到V’=V:
´在E集合当中选择最小的边<u,v>使得u∈V’但是v∉V’;
´V’加入节点v,E’加入<u,v>;
      对于上面的第二步,实际上我们只需要对于每一个点维护一个V’集合中的点到达该点的最短距离。
      然后每次扫描一遍数组找到我们所需要的v加入V’;
复杂度为O(N^2+M).
对于上面的第二步操作,我们实际上可以通过堆(优先队列)维护一个满足u∈V’但是v∉V’的边集,那么我们就能迅速取出满足要求的边;
然后当改变了V’的时候,我们就可以根据新加入的节点v对原有的堆进行删除和插入操作。
需要注意的是,当我们用优先队列实现的时候,我们需要将删除操作延迟。
复杂度为O((N+M)logN).
哔哔了这么多,我们直接来看看代码吧! (当然先是一些板子)
 

#include<bits/stdc++.h>
using namespace std;
const int maxn=10010;
int n,m,edge[maxn][maxn];
int dis[maxn];
bool boo[maxn];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
edge[x][y]=edge[y][x]=z;
}
dis[1]=0;
boo[1]=true;
for(int i=2;i<=n;++i)
dis[i]=123456789;
for(int i=2;i<=n;++i)
if(edge[1][i])
dis[i]=min(dis[i],edge[1][i]);
int ans=0;
for (int i=1;i<=n-1;++i)
{
int maxd=0;
for(int j=1;j<=n;++j)
{
if(!boo[j])
if(maxd==0||dis[maxd]>dis[j])
maxd=j;
}
boo[maxd]=true;
ans+=dis[maxd];
for(int j=1;j<=n;++j)
{
if(!boo[j])
if(edge[maxd][j])
dis[j]=min(dis[j],edge[maxd][j]);
}
}
printf("%d\n",ans);
return 0;
}

Kruskal算法
Kruskal算法主要分为两步:
给所有边按照边权从小到大的顺序排序;
从小到大依次考虑每条边(u,v)(最开始没有任何的边):
如果u与v已经连通了,那么加入(u,v)后会出现环,不添加;
如果u与v没有连通,那么加入(u,v)使其连通。
然后,对于判断是否联通,我们可以通过并查集来维护。

是不是感觉k算法比p算法好理解啊(暂且先那么叫)

据徐大佬说k算法好像比p算法快,唉,如果让你写的话,是不是一定选择这种又快有好写还有好理解的代码啊。

反正让我写的的话,我一定会选择写k算法的,但是这仅限于裸地最小生成树的题。

对于有的题k算法是不如p算法的,甚至有的时候,你用k算法还不一定能做出来。

好了,废话不多说了,我们来看看代码吧。

 

#include <bits/stdc++.h>
using namespace std;

const int maxn=100000+15;
struct Edge
{
    int x,y,z;
}edge[maxn];
bool cmp(Edge a,Edge b)
{
    return a.z<b.z;
}
int top[maxn],x,y,z,n,m;
int found(int x)
{
    if (top[x]==x) return x;
    top[x]=found(top[x]);
    return top[x];
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        edge[i].x=x;
        edge[i].y=y;
        edge[i].z=z;
    }
    sort(edge+1,edge+m+1,cmp);
    for (int i=1;i<=n;i++) top[i]=i;
    int ans=0;
    for (int i=1;i<=m;i++)
    {
        int x=edge[i].x,y=edge[i].y;
        int fx=found(x),fy=found(y);
        if (fx==fy) continue;
        top[fx]=fy;
        ans+=edge[i].z;
    }
    printf("%d\n",ans);
    return 0;
}

 

 

 

最小瓶颈生成树

有的童鞋看到这个就要抓狂了,这又是个什么鬼!!!

哎呀,不要着急,我们来具体看一看这个东西。

最小瓶颈生成树:在一个图中找出一棵树,使这棵树的最大权值最小。

给你一个这样的关系吧:最小生成树一定是最小瓶颈生成树,最小瓶颈生成树不一定是最小生成树。

最优比率生成树

给出一个图,每条边的权值是(a,b)的形式。求出一个生成树使得suma/sumb最小。
权值均为正值
´二分答案
´Suma/sumb>K
´Suma>Sumb*K,
´Sum(a-kb)>0
´一般的最大生成树
#include <bits/stdc++.h>
using namespace std;

const int maxn=100000+15;
struct Edge
{
    int x,y,a,b;
    Edge(int x=0,int y=0,int a=0,int b=0):
        x(x),y(y),a(a),b(b) {}
}edge[maxn],edge2[maxn];
int mst()
{
    return 0;
}
int change(int K)
{
    for (int i=1;i<=m;i++)
     edge2[i]=Edge(edge[i].x,edge[i].y,
      edge[i].a-K*edge[i].b,0);
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        int x,y,a,b;
        scanf("%d%d%d%d",&x,&y,&a,&b);
        edge[i]=Edge(x,y,a,b);
    }
    int l=0,r=1000000,mid;
    while (l+1<r)
    {
        mid=(l+r)/2;
        change(mid);
        if (mst()<=0) r=mid;
         else l=mid;
    }
    printf("%d\n",r);
    return 0;
}

 

转载于:https://www.cnblogs.com/z360/p/6853637.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值