洛谷4716 【模板】最小树形图

类似于最小生成树一样的东西

就是以给定的rt为根的最小外向生成树【内向直接建反边也行

然后算法是朱刘算法 大体过程就是先贪心选每个点的最小入边 然后暴力找环

没有环的话就是算法结束 返回即可 不然就是把这个环缩成一个点然后他的入边都要-=mn[v]就是拥有相同终点的边需要减掉变成增加量一样

如果要输出方案的话就需要还原 还原的话记录一下哪些边用过然后拆环即可

这个玩意时间复杂度貌似是O(nm)

但是我请教了一下巨佬们 大概这个是最劣复杂度 一般是不会达到的

可以理解为O(能过)就行了

代码如下。

//Love and Freedom.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 20021225
#define ll long long
#define N 110
#define M 10010
using namespace std;

struct edge{int u,v,c;}e[M];
int pre[N],mn[N],id[N],vis[N];
int n,m;
int zlal(int rt)
{
    int res = 0;
    while(1)
    {
        for(int i=1;i<=n;i++)    mn[i] = inf;
        for(int i=1;i<=m;i++)
            if(e[i].u != e[i].v && mn[e[i].v]>e[i].c)
            {
                mn[e[i].v] = e[i].c; pre[e[i].v] = e[i].u;
            }
        for(int i=1;i<=n;i++)    if(i!=rt && mn[i]==inf)    return -1;
        int tn = 0,u,v;
        memset(id,0,sizeof(id)); memset(vis,0,sizeof(vis));
        mn[rt] = 0;
        for(int i=1;i<=n;i++)
        {
            res += mn[i]; v=i;
            while(v!=rt && !id[v] && vis[v]!=i)
                vis[v] = i, v = pre[v];
            if(v!=rt && !id[v])
            {
                ++tn;
                for(u=pre[v];u!=v;u=pre[u])
                    id[u] = tn;
                id[v] = tn;
            }
        }
        if(!tn)    break;
        for(int i=1;i<=n;i++)    if(!id[i])
            id[i] = ++tn;
        for(int i=1;i<=m;i++)
        {
            v = e[i].v;
            e[i].u = id[e[i].u];
            e[i].v = id[e[i].v];
            if(e[i].u != e[i].v)
                e[i].c -= mn[v];
        }
        n = tn; rt = id[rt];
    }
    return res;
}
int main()
{
    int r;    scanf("%d%d%d",&n,&m,&r);
    for(int i=1;i<=m;i++)    scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c);
    printf("%d\n",zlal(r));
    return 0;
}
/**
3 2 1
1 3 0
3 2 1
*/
View Code

 

posted @ 2019-02-12 18:50  寒雨微凝  阅读(130)  评论(0编辑  收藏  举报