POJ_3164 最小树形图

http://poj.org/problem?id=3164/* 树形图是一棵有向树,它的根可以到达其他图的其他每个节点。最小树形图是一个图的所有树形图中 边权值最小的。 对于除了根节点外的每个节点 i,求出权值最小的入(1)边 (prev[i], i)。如果这些入边不构成环, 那么入边的集合 E 即为所求。假设其中的一个环为 v0 <- v1 <- …… <- vk <- v0, 那么如下更新原图: w(v[0], u) = min{w(v[j], u)} (0 <= j <= k) w(u, v[0]) = min{w(u, v[j]) - w(prev[j], j)} (0 <= j <= k) 并且 res 的值增加环中所有边的权值 重复以上操作,直到 E 中不存在环。此时 res 就是最小树形图的边权值之和。 w(u, v0) 之所以要修改为 min{w(u, v[j]) - w(prev[j], j)},因为如果环外有个点 u 连向环中 某一点 v[j],那么加上之前算入 res 总和的 w(prev[j], j),正好变成 w(u, v[j])。 */ #include<iostream> #include<cmath> #define INF 1000000000 using namespace std; double map[110][110]; bool visit[110],circle[110]; int pre[110]; int n,m; struct PT { double x,y; }p[110]; double dist(int i,int j) { return sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y)); } void dfs(int t) { if(visit[t]) return ; visit[t]=1; for(int i=1;i<=n;i++) if(map[t][i]<INF) dfs(i); } bool connect()//深搜,判断是否存在最小树形图 { dfs(1); for(int i=1;i<=n;i++) if(!visit[i]) return 0; return 1; } double solve() { double ans=0; int i,j,k; memset(circle,0,sizeof(circle));//如果某点被删掉,那么circle[i]=1 while(1) { for(i=2;i<=n;i++)//求出每个点的最小入边 { if(circle[i]) continue; map[i][i]=INF; pre[i]=i; for(j=1;j<=n;j++) { if(circle[j]) continue; if(map[j][i]<map[pre[i]][i]) pre[i]=j; } } for(i=2;i<=n;i++)//遍历找环 { if(circle[i]) continue; j=i; memset(visit,0,sizeof(visit)); while(!visit[j]&&j!=1) { visit[j]=1; j=pre[j]; } if(j==1)//j==1说明i不在环上 continue; i=j;//找到了环 ans+=map[pre[i]][i]; for(j=pre[i];j!=i;j=pre[j]) { ans+=map[pre[j]][j]; circle[j]=1;//用环上一点i代表此环,其他点删去,即circle[j]=1 } for(j=1;j<=n;j++) { if(circle[j]) continue; if(map[j][i]<INF) map[j][i]-=map[pre[i]][i];//更新j的入边 } for(j=pre[i];j!=i;j=pre[j])//环上所有点的最优边为人工顶点的边 { for(k=1;k<=n;k++) { if(circle[k]) continue; if(map[j][k]<INF) map[i][k]=min(map[i][k],map[j][k]); if(map[k][j]<INF) map[k][i]=min(map[k][i],map[k][j]-map[pre[j]][j]); } } break; } if(i>n) { for(j=2;j<=n;j++) { if(circle[j]) continue; ans+=map[pre[j]][j]; } break; } } return ans; } int main() { int i,j; int a,b; while(scanf("%d%d",&n,&m)!=EOF) { for(i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); for(i=1;i<=n;i++) for(j=1;j<=n;j++) map[i][j]=INF; for(i=1;i<=m;i++) { scanf("%d%d",&a,&b); map[a][b]=dist(a,b); } memset(visit,0,sizeof(visit)); if(!connect()) printf("poor snoopy/n"); else printf("%.2lf/n",solve()); } return 0; }

你可能感兴趣的:(POJ_3164 最小树形图)