《最小生成树》模板题————洛谷 P3366

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz

输入输出格式

输入格式:

 

第一行包含两个整数N、M,表示该图共有N个结点和M条无向边。(N<=5000,M<=200000)

接下来M行每行包含三个整数Xi、Yi、Zi,表示有一条长度为Zi的无向边连接结点Xi、Yi

 

输出格式:

 

输出包含一个数,即最小生成树的各边的长度之和;如果该图不连通则输出orz

 

输入输出样例

输入样例#1:

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

输出样例#1:

7

说明

时空限制:1000ms,128M

数据规模:

对于20%的数据:N<=5,M<=20

对于40%的数据:N<=50,M<=2500

对于70%的数据:N<=500,M<=10000

对于100%的数据:N<=5000,M<=200000

样例解释:

所以最小生成树的总边权为2+2+3=7

 

#include<bits/stdc++.h>
#include<cstring>
#include<queue>
using namespace std;

int vis[10001],dis[10001],head[10001];
int a,b,c,k,cnt,i,n,m,ans;		

/*
	<1> vis(visted)数组用来标记该点是否在生成树当中
	<2> dis(distance)数组用来记录该点到生成树的(现阶段)最小距离 
	<3> head数组用来记录以该点为起点的当前边的下标,若为-1,则说
	    明没有以该点为起点的边
*/

struct Edge
{
	int len_edge;	//记录边的长度 
	int point_sec;	//记录边的终点 
	int next_edge;	//记录同一顶点的前一个边的下标 
}e[400001];

void add_edge(int x,int y,int z)
{
	e[++k].point_sec=y;	//每次存边前,下标自增 
	e[k].len_edge=z; 
	e[k].next_edge=head[x];	//将同一顶点的前一个边的下标存进来 
	head[x]=k;	//用当前边的下标更新该顶点所指向的边 
} 

/*
	<1> 我们构建一个整型的二元组合用来记录当前边的长度和终点
	<2> 优先队列定义时的三个参数,变量,容器,升序(降序)
*/

typedef pair<int,int> pii;
priority_queue< pii,vector<pii>,greater<pii> > q;

void prim()
{
	dis[1]=0;			//将起点归入树中,故距离树的距离为0 
	q.push(make_pair(0,1));
/*	
	当队列为空时或者cnt==n时退出循环
	<1> 单纯第一种情况退出是因为存在点没有连通
	<2> 单纯第二种情况退出是因为所有点已经连通,已经生成最小树 
*/	
	while(!q.empty()&&cnt<n)
	{
		int len=q.top().first,point=q.top().second;
		q.pop();	//一定要记得将其pop掉 
/*
	取出权值最小的边,然后判断该边的终点是否已经被访问过,
	也就是已经在生成树中,若已经在生成树中,就直接continue
	如果没被访问过,那么cnt++(生成树中已有结点+1),并且将
	该点标记为访问过,生成树的(当前)权值也应加上该边的权值 
*/ 		
		if(vis[point])
			continue;
		cnt++;
		vis[point]=1;
		ans+=len;
/*
	从当前点为起点最后一个边的下标开始找,e[下标].next_edge
	就是以该点为起点的前一个边,直到next_edge的值为-1,说明
	前面没有以该点为起点的边了
*/			
		for(int i=head[point];i!=-1;i=e[i].next_edge)		
		{
/*	
	如果以该点为起点的边的权值小于终点到树的距离,那么以此权值
	来更新该终点到树的距离,并且将其压入队列当中,相当于用
	以此点为起点的边的权值来更新各终点到树的距离
*/			
			if(e[i].len_edge<dis[e[i].point_sec])
			{
				dis[e[i].point_sec]=e[i].len_edge;				
				q.push(make_pair(e[i].len_edge,e[i].point_sec));
			} 
		}
	}
}

int main()
{
/*
	<1> 将每个结点到生成树的权值初始化为无穷大
	<2> 将以每个点为起点的当前边的下标赋值为-1,
		也就是说没有以当前点为起点的边了
*/
	memset(dis,127,sizeof(dis));
	memset(head,-1,sizeof(head));
	
	scanf("%d%d",&n,&m);
	
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		add_edge(a,b,c);	//注意无向图要存两个方向的边 
		add_edge(b,a,c);
	} 
	
	prim();
	
	if(cnt==n)
		printf("%d\n",ans);
	else
		printf("orz\n");
	
	return 0;
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值