P4716 模板最小树形图
Posted popo-black-cat
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4716 模板最小树形图相关的知识,希望对你有一定的参考价值。
一道模版题(就是这个算法有点偏……)
这道题就是在一个有向图中,求出一个定根的有根树,使其边权之和最小,其实就是有向图的最小生成树。
其实挺简单的……没我想象的那么高深。就是在改边权的地方有点不好理解,正确性可以用数学归纳法证明。
一次次缩点直到这个图不再有环为止。
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<vector> #include<climits> using namespace std; #define maxn 20000 #define inf 999999999 struct node { int from,to,w; } edge[maxn]; int in[maxn],pre[maxn],id[maxn]; int vis[maxn]; int n,m,root,cnt; void add(int a,int b,int w) { edge[++cnt].to=b; edge[cnt].from=a; edge[cnt].w=w; } int zhuliu() { int ans=0; while(1) { for(int i=1; i<=n; i++) in[i]=inf; for(int i=1; i<=m; i++) { int u=edge[i].from; int v=edge[i].to; if(u!=v&&edge[i].w<in[v]) { in[v]=edge[i].w; pre[v]=u; } } for(int i=1; i<=n; i++) if(i!=root&&in[i]==inf) return -1; int num=0; memset(vis,0,sizeof(vis)); memset(id,0,sizeof(id)); for(int i=1;i<=n;i++) { if(i==root) continue; 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]=++num; for(int j=pre[v];j!=v;j=pre[j]) id[j]=num; } } if(!num) break; for(int i=1;i<=n;i++) if(!id[i]) id[i]=++num; for(int i=1;i<=m;i++) { int u=edge[i].from; int v=edge[i].to; edge[i].from=id[u]; edge[i].to=id[v]; if(id[u]!=id[v]) edge[i].w-=in[v]; } root=id[root]; n=num; } return ans; } int main() { scanf("%d%d%d",&n,&m,&root); for(int i=1; i<=m; i++) { int a,b,w; scanf("%d%d%d",&a,&b,&w); add(a,b,w); } printf("%d\n",zhuliu()); return 0; }
以上是关于P4716 模板最小树形图的主要内容,如果未能解决你的问题,请参考以下文章