MST(最小代价生成树)

生成树,即对图的dfs过程中,我们会得到一棵生成树,在所有不同的生成树中,会存在一些边上权值和最小的树,我们叫其最小代价生成树。

最小代价生成树常见算法有两种

prim  (点少)

bool vis[maxn];
int cost[maxn];
int prim(int u)
{
    memset(vis,0,sizeof(vis));
    for(int i=0;i<=n;i++)
        cost[i]=inf;

    cost[u]=0;
    int sum=0;
    while(true)
    {
        int v=-1;
        for(int i=0;i<=n;i++)
            if(vis[i]==false &&(v==-1||cost[i]<cost[v]) )
                v=i;

        if(v==-1)
            break;
        vis[v]=true;
        sum=sum+cost[v];

        for(int i=0;i<g[v].size();i++)
            if(vis[g[v][i].t]==false)
                cost[g[v][i].t]=min(cost[g[v][i].t],g[v][i].w);
    }
    return sum;
}

优先队列的优化:

struct point
{
    int p;
    int w;
    point(){}
    point(int a,int b):p(a),w(b){}
};
bool operator <(point a,point b)
{
    return a.w>b.w;
}

priority_queue<point> myque;
bool vis[maxn];
int prim(int u)
{
    memset(vis,0,sizeof(vis));
    while(!myque.empty())
        myque.pop();

    myque.push(point(u,0));
    int sum=0;
    while(!myque.empty())
    {
        point temp=myque.top();
        myque.pop();

        if(vis[temp.p]==true)
            continue;
        vis[temp.p]=true;
        sum=sum+temp.w;

        for(int i=0;i<g[temp.p].size();i++)
            if(vis[g[temp.p][i].t]==false)
                myque.push(point(g[temp.p][i].t,g[temp.p][i].w));
    }
    return sum;
}

 

kruskal (边少)

//每次选取的边
struct edge
{
    int f;int t;int w;
    edge(){}
    edge(int a,int b,int c):f(a),t(b),w(c){}
};
bool operator<(edge a,edge b)
{
    return a.w>b.w;
}

//并查集编号数组
int mset[maxn];
int find(int a)
{
    return mset[a]==a?a:find(mset[a]);
}
void uni(int a,int b)
{
    a=find(a);
    b=find(b);
    if(a<b)
        mset[b]=a;
    else
        mset[a]=b;
}

//每次选取最优的边
priority_queue<edge> que;
int kruscal()
{
    for(int i=1;i<=n;i++)  //如果有部分点不用连接或已连接,该代码初始化在函数外先执行
        mset[i]=i;
    while(!que.empty())
        que.pop();

    //加边到优先队列,采用vector写的也需要加
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(mmap[i][j]>0)
                que.push(edge(i,j,mmap[i][j]));
/*
   for(int i=1;i<=m;i++)
        que.push(e[i]);
*/

    int sum=0;
    while(!que.empty())
    {
        edge e=que.top();
        que.pop();
        if(find(e.f)==find(e.t))
            continue;

        uni(e.f,e.t);
        sum=sum+e.w;
    }

    /*  //处理连通性  快捷方法,即我们需要union的次数为边数即n-1次,用合并时cnt计数即可
    for(int i=1;i<=n;i++)
        if(find(mset[i])!=find(mset[1]))
            return -1;
    */

    return sum;
}

算法注意点:

1.kurskal算法能够很容易的合并不同联通分量的树,因此他在处理可能不连通的图的时候能够很容易的判断连通性

2.该算法即使在已经建立了部分的图上依然可以得到剩下生成树获得所需要的最小代价,因为我们只要发现目前边是最小,并且能够连接两个不同联通分量之间的树的时候,就加入该代价即可。

3.该算法在边较少的情况下非常优秀,但是如果说所给图的点数不多,但是边数几乎是完全图的边数,则可能很容易会导致超时等情况的产生(因为优先队列中的插值会排序),如果说即要处理边的情况,又给了很多边,则考虑使用邻接矩阵的prim算法,这样才能够即能处理连通边的情况,又能节约时间,缺点就是消耗空间。

如果要用kruskal算法处理较多边的情况,尽量不要用优先队列来处理顺序,直接将所有edge进行排序更快大概快20%

 

一些其他的生成树:

最大边权-最小边权 最小生成树

对于一个生成树来说,其中最大边权与最小边权之差最小的一个生成树的 最大边权-最小边权  是多少

对于这种生成树本质上没有比较好的策略去处理,故我们可以考虑每取一边,求每边不比它小的边形成的生成树,并找到该生成树中的最大边,不断维护该差值就好。由于是需要处理边,故用kruskal算法更加方便优秀。

而为了节约时间,我们可以考虑先对边排序,从最小边开始一个一个作为基准边,并在求kruskal中只加入边更大的到优先队列中(可不用优先队列,已经排好序)。再进行剪枝,因为生成树一定要至少n-1边选基准边的时候只需要选到留下至少n-1边即可

UVA - 1395

 

最小瓶颈生成树

最小瓶颈生成树,即在所有生成树中,使得最大边最小的那一颗生成树。

由上定义我们可以知道以下结论:

1.在一个图的最小生成树中,他一定也是最小瓶颈生成树,因为最大边不可能再小了。

2.同时我们也可以明显看出,最小瓶颈生成树不止一个,因为除了最大边以外的其他边的值可以改变。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值