规定根节点,求一颗生成树使得权值最小,但由于是有向图,所以最小生成树算法失效。
查资料后得知此类问题叫做最小树形图。
解决最小树形图问题的朱刘算法,算法核心基于找 【最小弧集->找环,消环缩点】 的思想,来慢慢构造树形图。
所有的灵魂都在这张图上。0.0
注意缩点后的弧权值的处理
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cmath> using namespace std; #define INF 0x3FFFFFF #define MAXN 2222 #define eps 1e-6 bool zero(double x) {return x>=-eps&&x<=eps;} struct edge { int u,v; double w; }e[10005]; int n,en,m; int pre[MAXN],id[MAXN],vis[MAXN]; double in[MAXN]; void add(int u,int v,double w) { e[en].u=u; e[en].v=v; e[en++].w=w; } int zl(int root ,int vn) { double ans=0; int cnt; while(1) { for(int i=0;i<vn;i++) in[i]=INF,id[i]=-1,vis[i]=-1; for(int i=0;i<en;i++) { if(in[e[i].v]>e[i].w && e[i].u!=e[i].v) { pre[e[i].v]=e[i].u; in[e[i].v]=e[i].w; } } in[root]=0; pre[root]=root; for(int i=0;i<vn;i++) { ans+=in[i]; if(zero(in[i]-INF)){puts("poor snoopy"); return -1; } } cnt=0; for(int i=0;i<vn;i++) { if(vis[i]==-1) { int t=i; while(vis[t]==-1) { vis[t]=i; t=pre[t]; } if(vis[t]!=i || t==root) continue; for(int j=pre[t];j!=t;j=pre[j]) id[j]=cnt; id[t]=cnt++; } } if(cnt==0) break; for(int i=0;i<vn;i++) if(id[i]==-1) id[i]=cnt++; for(int i=0;i<en;i++) { int u,v; u=e[i].u; v=e[i].v; e[i].u=id[u]; e[i].v=id[v]; e[i].w-=in[v]; } vn=cnt; root=id[root]; } printf("%.2lf\n",ans); return ans; } double x[105],y[105]; int main() { int a,b; while(~scanf("%d%d",&n,&m)) { for(int i=0;i<n;i++) { scanf("%lf%lf",&x[i],&y[i]); } en=0; for(int i=1;i<=m;i++) { scanf("%d%d",&a,&b); a--; b--; double d=sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b])); add(a,b,d); } zl(0,n); } return 0; }