最小树形图
即:有向图中的最小生成树(外向树)
大致思路
1.对每个点求出边权最小的入边,并记录
2.看1,中组成的图有无环,且能否组成树
3.若存在环,则将环缩成一个点,重新赋边权,回到1.
代码
(洛谷模板)
#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define get getchar()
#define in inline
in int read()
{
int t=0; char ch=get;
while(ch<'0' || ch>'9') ch=get;
while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
return t;
}
const int _=1e4+23;
const int inf=0x3f3f3f3f;
int rt, n, m;
struct E{
int u, v, w;
}ed[_];
int id[_], mn[_], pre[_], vis[_];
ll ans=0;
in void zhuliu(int n)
{
while(1)
{
int cnt=0;
for(re int i=1;i<=n;++i) mn[i]=inf;
for(re int i=1;i<=m;++i) //找到最小入边
if(ed[i].w<mn[ed[i].v] && ed[i].u!=ed[i].v)
mn[ed[i].v]=ed[i].w, pre[ed[i].v]=ed[i].u;
for(re int i=1;i<=n;++i) if(i!=rt && mn[i]==inf) {ans=-1;return ;} //判断是否能组成外向树
for(re int i=1;i<=n;++i) vis[i]=id[i]=0;
for(re int i=1;i<=n;++i)
{
if(i==rt) continue;
ans+=mn[i];
int v=i;
while(vis[v]!=i && !id[v] && v!=rt) vis[v]=i, v=pre[v]; //找环
if(!id[v] && v!=rt) //把环缩点
{
id[v]=++cnt;
for(re int u=pre[v];u!=v;u=pre[u]) id[u]=cnt;
}
}
if(cnt==0) break;
for(re int i=1;i<=n;++i) if(!id[i]) id[i]=++cnt;
for(re int i=1;i<=m;++i) //重新赋边权与标号
{
int u=ed[i].u, v=ed[i].v;
ed[i].u=id[u], ed[i].v=id[v];
if(ed[i].u!=ed[i].v) ed[i].w-=mn[v];
}
rt=id[rt], n=cnt;
}
return ;
}
int main()
{
n=read(), m=read(), rt=read();
for(re int i=1;i<=m;++i) ed[i].u=read(), ed[i].v=read(), ed[i].w=read();
zhuliu(n);
printf("%lld\n", ans);
return 0;
}