解析: 裸的有向图最小生成树
代码
#include<cstdio> #include<cstring> #include<string> #include<iostream> #include<sstream> #include<algorithm> #include<utility> #include<vector> #include<set> #include<map> #include<queue> #include<cmath> #include<iterator> #include<stack> using namespace std; const int INF=1e9+7; const int eps=0.0000001; const int maxn=1005; struct edge { int u,v,w; edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){} }E[40005]; int pre[maxn],InEdge[maxn],vis[maxn],id[maxn]; int Dir_MST(int root,int Vcnt,int Ecnt) { int ret=0; while(true) { for(int i=1;i<=Vcnt;i++) InEdge[i]=INF; for(int i=1;i<=Ecnt;i++) { edge& e=E[i]; int u=e.u,v=e.v,w=e.w; if(u==v) continue; if(w<InEdge[v]) { InEdge[v]=w; pre[v]=u; } //找最小的指向v的边 } InEdge[root]=0; for(int i=1;i<=Vcnt;i++) if(i!=root&&InEdge[i]==INF) return -1;//存在某个点跟整个图分离 int ID=0; for(int i=1;i<=Vcnt;i++) vis[i]=id[i]=-1; for(int i=1;i<=Vcnt;i++) { ret+=InEdge[i]; //把那些边加进答案 int a=i; while(vis[a]!=i&&id[a]==-1&&a!=root){ vis[a]=i; a=pre[a]; } if(id[a]==-1&&a!=root) { ++ID; for(int b=pre[a];b!=a;b=pre[b]) id[b]=ID; //重新编号 id[a]=ID; } } if(ID==0) return ret; //找到解 for(int i=1;i<=Vcnt;i++) if(id[i]==-1) id[i]=++ID; //独立的点编号 for(int i=1;i<=Ecnt;i++) { edge& e=E[i]; int u=e.u,v=e.v; e.u=id[u]; e.v=id[v]; if(id[u]!=id[v]) e.w-=InEdge[v]; //之前加的那一部分要减掉 } Vcnt=ID; root=id[root]; } } int main() { int T,Case=0; scanf("%d",&T); while(T--) { int N,M,u,v,w; scanf("%d%d",&N,&M); for(int i=1;i<=M;i++) { scanf("%d%d%d",&u,&v,&w); u++; v++; E[i]=edge(u,v,w); } int ans=Dir_MST(1,N,M); printf("Case #%d: ",++Case); if(ans==-1) printf("Possums! "); else printf("%d ",ans); } return 0; }