最小树形图(刘朱算法)记录

来源:互联网 发布:中国经济走势 知乎 编辑:程序博客网 时间:2024/06/06 08:45

算法步骤:

1、建立最短边集(有向边), 注意除去自环(自己连向自己)
in[v] 记录最小权值,pre[v] 记录边的起点
例子:
边1 A->B 权:5
边2 C->B 权:3

则 in[B]=3 pre[B]=C

若集合建立完毕后,仍有孤立的点,则不存在最小树形图。

2、查找有向环,并把它缩成一个点
方法: 设置环中点的编号 id[x]=newnode;

3、若没有 有向环则已经构建最小树形图 ,输出结果

否则,把剩余不在环中的点收集起来。
再更新边
int v=Edge.to;
Edge.from=id[Edge.from]
Edge.to=id[Edge.to]
重要:
如果不构成自环,即Edge.from!=Edge.to
则 Edge.cost-=in[v]

模板:

// 假设点集1-n  边集0- m-1  起点是1#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>using namespace std;const int maxn=200;const int maxm=1e4+10;const int INF=0x3f3f3f3f;struct Edge{    int from,to,cost;    Edge() {}    Edge(int f,int t,int c):from(f),to(t),cost(c) {} }edges[maxm]; int pre[maxn];int n,m;int in[maxn],id[maxn],vis[maxn];  //vis记录环void init()       //建图{    scanf("%d%d",&n,&m); int cnt=0;    for (int i=0;i<m;i++)    {        int u,v,c;        scanf("%d%d%d",&u,&v,&c);        if (u!=v) edges[cnt++]=Edge(u,v,c);  //除去自环    } m=cnt;}int work(int s)        //最小树形图算法{    int ret=0;    while(1)    {        for (int i=1;i<=n;i++) in[i]=INF;       //建立最短边集        for (int i=0;i<m;i++) {            Edge e=edges[i];            if (e.to!=e.from && in[e.to]>e.cost) {   //易错!! e.to!=e.from  自环                pre[e.to]=e.from;                in[e.to]=e.cost;            }        }        for (int i=1;i<=n;i++) {            if (i!=s&&in[i]==INF) return -1;        //存在孤立点  不存在最小树形图        }        int np=in[s]=0;        memset(vis,-1,sizeof(vis));        memset(id,-1,sizeof(id));        for (int i=1;i<=n;i++) {            ret+=in[i];            int v=i;            while (vis[v]!=i&&id[v]==-1&&v!=s) {   //不找到环  不存在其他环中   不是根                vis[v]=i;                v=pre[v];            }            if (id[v]==-1&&v!=s) {            //找到有向环(因第一个条件而跳出while, 即满足后两个条件)               np++;                for (int u=pre[v];u!=v;u=pre[u]) {                    id[u]=np;                }                id[v]=np;            }        }        if (np==0) break;        for (int i=1;i<=n;i++)         //收集非环点            if (id[i]==-1) id[i]=++np;        for (int i=0;i<m;i++) {          //重新处理边            int u=edges[i].from;            int v=edges[i].to;            edges[i].from=id[edges[i].from];            edges[i].to=id[edges[i].to];            if (edges[i].from!=edges[i].to) {                edges[i].cost-=in[v];            }        }        n=np; s=id[s];             //新点数,新起点    }    return ret;}int main(){    init();    int ans=work(1);    if (~ans) printf("%d\n",ans);        else printf("impossible\n");     return 0;}
0 0
原创粉丝点击