题目描述
给定包含 n 个结点, m条有向边的一个图。试求一棵以结点 r 为根的最小树形图,并输出最小树形图每条边的权值之和,如果没有以 r 为根的最小树形图,输出 -1。
输入格式
第一行包含三个整数 n,m,r,意义同题目所述。
接下来 m 行,每行包含三个整数 u,v,w表示图中存在一条从 u 指向 v 的权值为 w的有向边。
输出格式
如果原图中存在以 rr 为根的最小树形图,就输出最小树形图每条边的权值之和,否则输出 -1。
输入输出样例
输入
4 6 1 1 2 3 1 3 1 4 1 2 4 2 2 3 2 1 3 4 1
输出 #
3
输入 #2
4 6 3 1 2 3 1 3 1 4 1 2 4 2 2 3 2 1 3 4 1
输出 #2
4
输入 #3
4 6 2 1 2 3 1 3 1 4 1 2 4 2 2 3 2 1 3 4 1
输出 #3
-1
说明/提示
样例 1 解释
最小树形图中包含第 2, 5, 6 三条边,总权值为1+1+1=3
样例 2 解释
最小树形图中包含第 3, 5, 6 三条边,总权值为 2+1+1=3
样例 3 解释
无法构成最小树形图,故输出 -1 。
#include <bits/stdc++.h> using namespace std; const int inf=2e9; const int maxn=10010; int n,m,root; int head[1000],in[1000],pre[1000],vis[1000],id[1000]; long long ans; struct node{ int from,to,val; }E[maxn]; long long mintree() { while (1) { for (int i=1; i<=n; i++) { in[i]=inf; } for (int i=1; i<=m; i++) { if (E[i].from!=E[i].to&&E[i].val<in[E[i].to]) { in[E[i].to]=E[i].val; pre[E[i].to]=E[i].from; } } for (int i=1; i<=n; i++) { if (in[i]==inf&&i!=root) { return -1; } } int cnt=0; memset(vis,0,sizeof(vis)); memset(id,0,sizeof(id)); in[root]=0; for (int i=1; i<=n; i++) { ans+=in[i]; int v=i; while (vis[v]!=i&&id[v]==0&&v!=root) { vis[v]=i; v=pre[v]; } //找环 if (v!=root&&id[v]==0) { id[v]=++cnt; for (int u=pre[v]; u!=v; u=pre[u]) { id[u]=cnt; } } //对环进行收缩 } if (!cnt) { break; } for (int i=1;i<=n;i++){ if (!id[i]){ id[i]=++cnt; } } for (int i=1;i<=m;i++){ int u=E[i].from,v=E[i].to; E[i].from=id[E[i].from]; E[i].to=id[E[i].to]; if (u!=v){ E[i].val-=in[v]; } }//新建图 n=cnt; root=id[root]; } return ans; } int main() { scanf("%d%d%d",&n,&m,&root); for (int i=1; i<=m; i++) { scanf("%d%d%d",&E[i].from,&E[i].to,&E[i].val); } printf("%lld ",mintree()); }