洛谷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 */