【题目链接】 http://acm.hdu.edu.cn/showproblem.php?pid=6141
【题目大意】
给出一个有向图,求1点为根的最小树形图使得第n个点的直接父亲编号最小
【题解】
如果没有第n个点直接父亲编号最小的要求,
那么只要跑一遍朱刘算法即可,考虑到直接父亲最小的条件,
我们连向第n个点的所有边进行加权操作,
使得其在总边权相同的情况选取答案具有优先性
【代码】
#include <cstdio> #include <algorithm> using namespace std; typedef long long LL; const int N=1010,M=10010; const LL INF=0x3f3f3f3f3f3f3f3f; int ROOT; struct DMST{ int n,size,pre[N],id[N],vis[N]; LL in[N]; struct EDGE{ int u,v; LL cost; EDGE(){} EDGE(int a,int b,int c):u(a),v(b),cost(c){} }E[M]; void init(int _n){n=_n,size=0;} void add(int u,int v,int w){E[size++]=EDGE(u,v,w);} LL dmst(int root){ int u,v,cnt; LL ret=0; while(1){ for(int i=0;i<n;i++)in[i]=INF; for(int i=0;i<size;i++){ u=E[i].u,v=E[i].v; if(E[i].cost<in[v]&&u!=v){ pre[v]=u,in[v]=E[i].cost; if(u==root)ROOT=i; } } for(int i=0;i<n;i++)if(i!=root&&in[i]==INF)return -1; cnt=in[root]=0; for(int i=0;i<n;i++)id[i]=vis[i]=-1; for(int i=0;i<n;i++){ ret+=in[i],v=i; while(vis[v]!=i&&id[v]==-1&&v!=root)vis[v]=i,v=pre[v]; if(v!=root&&id[v]==-1){ for(u=pre[v];u!=v;u=pre[u])id[u]=cnt; id[v]=cnt++; } } if(!cnt)break; for(int i=0;i<n;i++)if(id[i]==-1)id[i]=cnt++; for(int i=0;v=E[i].v,i<size;i++){ E[i].u=id[E[i].u],E[i].v=id[E[i].v]; if(E[i].u!=E[i].v)E[i].cost-=in[v]; }n=cnt,root=id[root]; }return ret; } }U; int T,n,m; int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); U.init(n); while(m--){ int x,y,z; scanf("%d%d%d",&x,&y,&z); z*=1000; if(y==n)z+=n-x; U.add(x-1,y-1,-z); }LL ans=-U.dmst(0); int fa=n-ans%1000; ans/=1000; printf("%lld %d ",ans,fa); }return 0; }