zoukankan      html  css  js  c++  java
  • 朱刘算法

    有这样一类问题:一个n个点m条边的有向图中,找到一颗总边权最小的生成树,使得根节点能到达任意一个点(这样的一颗树就叫做这个图的最小树形图);

    我们怎么暴力怎么来:

    1.首先,我们对于图中的每个点y求出所有出边指向y中边权最小的点x,对于(x,y)建立父子关系;2.

    2.然后我们按照这个关系得到一个图,由于自环不可能出现在生成树中,所有清除所有的自环;

    3.如果这个图不存在强联通分量(环),那么这棵树就是一个最小树形图;

    4.如果不是呢?我们将图中每个强联通分量缩成一个点,表示这个强联通分量中所有边都会选择;

    5.然显然,这个强联通分量中不可能选取所有的边,那么采取可反悔贪心的思想:把所有出边指向这个(强联通分量中的点x)的(边权w)减去(与点x存在父子关系的点y)之间的(边权k),这意味着如果以后要选择边w,那么就要断去边k,额外的代价就是w-k;

    6.然后缩完点后得到一个新的图,重复步骤1,直到出现步骤3,此时的总代价便是最小树形图的边权最小值;

    这就是朱刘算法(其实本质就带反悔贪心的暴力啦~)

    #include <cstdio>
    
    using namespace std;
    
    typedef long long ll;
    
    const int maxn=1e2+50;
    const int maxm=1e4+50;
    const int inf=0x3f3f3f3f;
    
    int n,m,r;
    ll ans;
    
    inline int read() {
        int a=0;char c=getchar();
        while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9') a=(a<<1)+(a<<3)+c-'0',c=getchar();
        return a;
    }
    
    struct edge {int u,v,w;}e[maxm];
    
    int cnt,fa[maxn],id[maxn],top[maxn],min[maxn];
    //cnt当前图环的数量 
    //id[u]代表u节点在第id[u]个环中
    //top[u]代表u所在链的代表元素 类似并查集 
    //min[u]为当前连到u点的最短边的边权 fa[v]当前连到v点的最短边的u 
    
    inline int getans() {
        while(1) {
            for(register int i=1;i<=n;++i) id[i]=top[i]=0,min[i]=inf;
            for(register int i=1;i<=m;++i)
                if(e[i].u!=e[i].v&&e[i].w<min[e[i].v])
                //不是自环 并且边权比选定的还小 
                    fa[e[i].v]=e[i].u,min[e[i].v]=e[i].w;
            int u=min[r]=0;
            for(register int i=1;i<=n;++i) {
                if(min[i]==inf) return 0; //存在一个不可以连接的点 
                ans+=min[i];
                for(u=i;u!=r&&top[u]!=i&&!id[u];u=fa[u]) top[u]=i;
                //找到包含不在环中的点最多的链 打上标记 
                if(u!=r&&!id[u]) { //这时候还满足条件说明vis[u]==i 即成环 
                    id[u]=++cnt;
                    for(int v=fa[u];v!=u;v=fa[v]) id[v]=cnt;
                }
            }if(!cnt) return 1; //没环就是找到答案了 
            for(register int i=1;i<=n;++i) if(!id[i]) id[i]=++cnt;
            //i节点不存在当前树中 就给他自己成一个环 
            for(register int i=1;i<=m;++i) {
                int last=min[e[i].v];
                //last等于当前连进v点的边的最小权值 
                if((e[i].u=id[e[i].u])!=(e[i].v=id[e[i].v])) e[i].w-=last;
                //当前边的两个端点不在同一个环内 
            }n=cnt;r=id[r];cnt=0;
            //缩完点后 当前点数就为环数 根节点就是根节点所在的环 
        }
    }
    
    int main() {
        n=read();m=read();r=read();
        for(register int i=0;i<m;e[++i]=(edge){read(),read(),read()});
        if(getans()) printf("%lld",ans);
        else printf("-1");
        return 0;
    }
    
  • 相关阅读:
    perl学习之路3
    perl学习之路1
    年少的忧伤
    莎士比亚的情诗
    自做贪吃蛇游戏的android实现
    伤感的蝴蝶
    爱上下拉列表框Spinner
    相识RadioGroup初恋CheckBox
    EditText和Button的纠缠
    谁陪我一起打包Andriod应用
  • 原文地址:https://www.cnblogs.com/kamimxr/p/12035724.html
Copyright © 2011-2022 走看看