Command Network POJ - 3164(最小树形图)

题目链接

大意:给你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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值