大意:给你n个点的坐标,再给你m条边。问你这个图的最小生成树。
思路:因为这个图是有向图,并且也给了定根,所以要用朱刘算法。参考博客
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 105
#define inf 0x3f3f3f3f
using namespace std;
struct node{
int u,v;
double w;
}edge[maxn*maxn];
int n,m;
int pre[maxn],vis[maxn],id[maxn];//pre存储父节点,vis标记作用,id记录结点i所在环的编号
double in[maxn];//in记录i入边中最小的权值
double x[maxn],y[maxn];
double dis(int a,int b){
return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
double zhuliu(int root){
double ans=0;
while(true){
for(int i=0;i<n;i++){
in[i]=inf;//初始化
}
for(int i=0;i<m;i++){
int u=edge[i].u;
int v=edge[i].v;
if(edge[i].w<in[v]&&u!=v){
pre[v]=u;
in[v]=edge[i].w;
}
}
for(int i=0;i<n;i++){
if(i==root)continue;
if(in[i]==inf){
return -1;//有其他孤立点 则不存在最小树形图
}
}
//找有向环
int cnt=0;//记录当前查找中 环的总数
memset(id,-1,sizeof id);
memset(vis,-1,sizeof vis);
in[root]=0;//根
for(int i=0;i<n;i++){
ans+=in[i];//累加
int v=i;
//找图中的有向环 三种情况会终止while循环
//1,直到出现带有同样标记的点说明成环
//2,节点已经属于其他环
//3,遍历到根
while(vis[v]!=i&&id[v]==-1&&v!=root){
vis[v]=i;//标记
v=pre[v];//一直向上找
}
//因为找到某节点属于其他环 或者 遍历到根 说明当前没有找到有向环
if(v!=root&&id[v]==-1){必须上述查找已经找到有向环
for(int j=pre[v];j!=v;j=pre[j]){
id[j]=cnt;
}
id[v]=cnt++;
}
}
if(cnt==0){//不存在有向环
break;
}
//可能存在独立点
for(int i=0;i<n;i++){
if(id[i]==-1){
id[i]=cnt++;
}
}
for(int i=0;i<m;i++){
int v=edge[i].v;
edge[i].u=id[edge[i].u];
edge[i].v=id[edge[i].v];
//两点不在同一个环 u到v的距离为 边权cost - in[v](入点v的最小值)
if(edge[i].u!=edge[i].v){
edge[i].w-=in[v];//更新边权值 继续下一条边的判定
}
}
n=cnt;//以环总数为下次操作的点数 继续执行上述操作 直到没有环
root=id[root];
}
return ans;
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<n;i++){
scanf("%lf%lf",&x[i],&y[i]);
}
for(int i=0;i<m;i++){
scanf("%d%d",&edge[i].u,&edge[i].v);
edge[i].u--;
edge[i].v--;
if(edge[i].u!=edge[i].v){
edge[i].w=dis(edge[i].u,edge[i].v);
}else{
edge[i].w=inf;
}
}
double ans=zhuliu(0);
if(ans==-1){
printf("poor snoopy\n");
}else{
printf("%.2lf\n",ans);
}
}
return 0;
}