码迷,mamicode.com
首页 > 其他好文 > 详细

【图论】MST及拓展

时间:2019-09-06 17:27:57      阅读:108      评论:0      收藏:0      [点我收藏+]

标签:style   包括   void   拓展   const   nod   联通   转换   node   

MST的三种解法

1>prim

2>kruskal

算法步骤
步骤一:T是边的集合,其初始状态为空;
步骤二:从原图剩余边中选取一条最小代价的边;
步骤三:看其是否与当前T中其它边构成环路;
步骤四:如果未构成环路,则加入T中;否则,丢弃该边;
步骤五:是否还有剩余边,如果有则返回步骤二,否则,程序结束。

算法证明(来自网络)

(1)Kruskal算法一定能得到一个生成树;
(2)该生成树具有最小代价。

=》
(1)假设该算法得到的不是生成树,根据树的定义,就有两种情形,第一是得到的图是有环的,第二就是得到的图是不连通的。由于算法要求每次加入边都是无环的,所以第一种情形不存在,下面就只需证明第二种情形不存在即可。
假设得到的图是不连通的,则至少包含两个独立的的边集,假设其中一个为E,则E中边对应的所有点都无法到达其它边集对应的点
(否则,根据算法定义,相应的联系边应被加入树中),而这与原图是连通的产生矛盾,因此,第二种情形也不可能存在。得证。
(2)假设图有n个顶点,则生成树一定具有n-1条边.由于图的生成树个数是有限的,所以至少有一棵树具有最小代价,假设该树为U。
先做出如下假设:
1)得到的树为T。
2)U和T中不同的边的条数为k,其它n-1-k条边相同,这n-1-k条边构成边集E。;
3)在T中而不在U中的边按代价从小到大依次为a1,a2,...,ak。
4)在U中而不在T中的边按代价从小到大依次为x1,x2,...,xk。
现在我们通过把U转换为T(把T的边依次移入U中),来证明U和T具有相同代价。
首先,我们将a1移入U中,由于U本身是一棵树,此时任意加一条边都构成回路,所以a1的加入必然产生一条回路,且这条回路必然包括x1,x2,...,xk中的边。
(否则a1与E中的边构成回路,而E也在T中,这与T中无回路矛盾。)
在这个回路中删除属于x1,x2,...,xk且代价最大边xi构成一个新的生成树V。
假设a1代价小于xi,则V的代价小于U,这与U是最小代价树矛盾,所以a1不可能小于xi。
假设a1大于xi,按照Kruskal算法,首先考虑代价小的边,则执行Kruskal算法时,xi应该是在a1之前考虑,而a1又在a2,...,ak之前考虑,所以考虑xi之前,T中的边只能是E中的边,而xi既然没加入树T,就说明xi必然与E中的某些边构成回路,但xi和E又同时在U中,这与U是生成树矛盾,所以a1也不可能大于xi。
因此,新得到的树V与T具有相同代价。
依次类推,把a1,a2,...,ak的边逐渐加到U中,最终得到的树(T)与U代价相同。
证明结束。

3>破圈

 

 

<1>最小生成树计数

kruskal+并查集

想象:
把图分成两部分,一部分是环,一部分是链

从破圈法反过来:
一个环,假设是1-2-3(-1)
我现在破圈,就是找里面最大的除去,
但是K,就是找前两条小的边

然后链,因为不连通,所以都要加上

那么如果我想在这个过程中加边,
就是一个环中多个边的值一样,选择多样,
然后找个办法,找出一环环,环上环的选择数

 

据说这是一个定理
1.不同的最小生成树中,每种权值的边出现的个数是确定的。
2.不同的生成树中,某一种权值的边连接完成后,形成的联通块状态是一样的 。

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
const int N=103,M=1003,mod=31011;
struct node
{
    int u,v,w;
    bool operator < (const node & o) const
    { return w<o.w; }
} sd[M];
struct nd
{
    int l,r;
    int tm,val;
}dd[M];
int fa[N];
int find(int x)
{ return !fa[x] ?x : find(fa[x]); }//这道题不能路径压缩,???? 

int sum,goal,r;
void dfs(int nw,int kk)
{
    if(kk==goal)
    {
        sum++;
        return ;
    }
    if(nw>=r) return ;
    else
    {
        int fu=find(sd[nw].u ),fv=find(sd[nw].v );
        if(fu!=fv) 
            fa[fu]=fv,dfs(nw+1,kk+1),fa[fu]=fa[fv]=0;
    }
    dfs(nw+1,kk);
}

int ans;
int main()
{    
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
        scanf("%d%d%d",&sd[i].u ,&sd[i].v ,&sd[i].w );
    sort(sd,sd+m);
    
    int cnt=1,pos=0;
    for(int i=0;i<m;i++)//我在这放了一个cnt,然后去掉了不少可能的结果。。。 
    {
        if(sd[i].w !=dd[pos].val ) dd[++pos].val =sd[i].w ,dd[pos].l =dd[pos-1].r =i;
        int fu=find(sd[i].u ),fv=find(sd[i].v );
        if(fu^fv) 
            fa[fu]=fv,dd[pos].tm ++,cnt++;
    }dd[pos].r =m;
    if(cnt^n)
    {
        printf("0\n");
        return 0;
    }
    else ans=1;
    
    memset(fa,0,sizeof(fa));
    for(int i=1;i<=pos;i++)
    {
        if(!dd[i].tm ) continue;
        //if(dd[i].r -dd[i].l ==dd[i].tm ) continue;//我这里计算,想减掉枝,忘了让fa更新,然后一直wa 
        sum=0,goal=dd[i].tm ,r=dd[i].r ;
        dfs(dd[i].l ,0);
        ans=ans*sum%mod;
        for(int j=dd[i].l ;j<dd[i].r ;j++)
        {
            int fu=find(sd[j].u ),fv=find(sd[j].v );
            if(fu!=fv) fa[fu]=fv;
        }
    }
    printf("%d",ans);
    
    return 0;
}

<2>黑暗城堡

 

【图论】MST及拓展

标签:style   包括   void   拓展   const   nod   联通   转换   node   

原文地址:https://www.cnblogs.com/xwww666666/p/11476958.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!