【POJ3164】Command Network 最小树形图模板题 重修版
链接:
#include <stdio.h>
int main()
{
puts("转载请注明出处[vmurder]谢谢");
puts("网址:blog.csdn.net/vmurder/article/details/44935891");
}
我以前的版本
算法构造过程以及傻叉代码+弱版注释见以前博客
http://blog.csdn.net/vmurder/article/details/38819711
最小树形图:
名词解释:
算法流程:
- 对于一张有向图,除了根以外每个点都找一条权值最小的入边(称之为 [最小弧] )
- 这样就可能构成了一片由基环树组成的森林,然后我们把每个环都缩点,并根据点的新编号重建图。但是如果没有环(所有点与根连通),则最小树形图已经建成,跳出流程。
如果计算边权:
每次将所有点(根除外)最小弧长度累加,重建图时与x相连的边权值全部减去此最小弧长度,这样就保证了最后一个点的流程中加得的所有最小弧长度等于最后一次加的边的原长度。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
#define N 200
#define M 50000
using namespace std;
struct Fiona
{
double x,y;
}s[N];
struct Syndra
{
int u,v;
double w;
}e[M];
int pre[N],f[N],vis[N];
double l[N];
double dist(Fiona A,Fiona B)
{
return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
double Directed_MST(int root,int n,int m)
{
int i,j,k;
int u,v,cnt;
double ans=0;
while(1)
{
for(i=1;i<=n;i++)f[i]=vis[i]=0,l[i]=inf;
for(i=1;i<=m;i++)
{
u=e[i].u;v=e[i].v;
if(l[v]>e[i].w)pre[v]=u,l[v]=e[i].w;
}
for(i=1;i<=n;i++)if(l[i]==inf&&i!=root)return -1;
l[root]=cnt=0;
for(i=1;i<=n;i++)
{
ans+=l[i];
if(vis[i])continue;
for(v=i;!vis[v]&&v!=root;vis[v]=i,v=pre[v]);
if(v!=root&&vis[v]==i)
{
f[v]=++cnt;
for(u=pre[v];u!=v;u=pre[u])f[u]=cnt;
}
}
if(!cnt)break;
for(i=1;i<=n;i++)if(!f[i])f[i]=++cnt;
n=cnt,cnt=0,root=f[root];
for(i=1;i<=m;i++)
{
u=e[i].u,v=e[i].v;
if(f[u]!=f[v])
{
e[++cnt].u=f[u];
e[cnt].v=f[v];
e[cnt].w=e[i].w-l[v];
}
}
m=cnt;
}
return ans;
}
int main()
{
freopen("test.in","r",stdin);
int i,n,m;
double ans;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=1;i<=n;i++)scanf("%lf%lf",&s[i].x,&s[i].y);
for(i=1;i<=m;i++)
{
scanf("%d%d",&e[i].u,&e[i].v);
if(e[i].u!=e[i].v)e[i].w=dist(s[e[i].u],s[e[i].v]);
else i--,m--;
}
ans=Directed_MST(1,n,m);
if(ans==-1)printf("poor snoopy\n");
else printf("%.2f\n",ans);
}
return 0;
}
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。