题意:给你n个点坐标,m个连接关系,问从1要能到所以点至少要铺设多少长的电线。
个人心得:第一次做最小树形图。
思路:前人博客
关于朱刘算法,主要思路就是一下几步:
1、对于每个点(除根节点),找最小入边。
2、判环,将环所成新点。
3、重建图。然后重复以上步骤直到没环。
详见链接。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 1<<30
using namespace std;
struct node
{
double x,y;
}p[105];
struct edge
{
int u,v;
double l;
}e[10005];
int m,n,id[105],vis[105],pre[105];
double in[105];
double DMST(int root,int vn,int en)
{
int u,v;
double ans=0;
while(1)
{
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
int cnt=0;
for(int i=1;i<=vn;i++)in[i]=maxn;
for(int i=1;i<=en;i++)
{
u=e[i].u;
v=e[i].v;
if(e[i].l<in[v]&&u!=v)
{
in[v]=e[i].l;
pre[v]=u;
}
}
for(int i=1;i<=vn;i++)
{
if(i==root)continue;
else if(in[i]==maxn)return -1;
}
in[root]=0;
for(int i=1;i<=vn;i++)
{
ans+=in[i];
int v=i;
while(vis[v]!=i&&id[v]==-1&&v!=root)
{
vis[v]=i;
v=pre[v];
}
if(v!=root&&id[v]==-1)
{
u=v;
do
{
id[u]=cnt+1;
u=pre[u];
}while(u!=v);
cnt++;
}
}
if(cnt==0)break;
for(int i=1;i<=vn;i++)
{
if(id[i]==-1)
{
id[i]=++cnt;
}
}
for(int i=1;i<=en;i++)
{
u=e[i].u;
v=e[i].v;
e[i].u=id[u];
e[i].v=id[v];
if(id[u]!=id[v])e[i].l-=in[v];
}
vn=cnt;
root=id[root];
}
return ans;
}
int main()
{
int u,v;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
e[i].u=u;
e[i].v=v;
e[i].l=sqrt((p[u].x-p[v].x)*(p[u].x-p[v].x)+(p[u].y-p[v].y)*(p[u].y-p[v].y));
}
double ans=DMST(1,n,m);
if(ans==-1)printf("poor snoopy\n");
else printf("%.2f\n",ans);
}
return 0;
}