Prim算法求最小生成树

17 篇文章 0 订阅

求总权值

例题

思想类似迪杰斯特拉算法,贪心,两边之和大于第三边

不过权值可以是负数

写题的时候数组一定要开大,开大,开大!

#include<cstdio>//优化后外层循环为n,内层循环1为优先队列push的logn,2一共为边数m乘以push的logn ,时间复杂度由n^2降为logn(n+m)
#include<iostream>//数组从1开始而不是0 
#include<cstring>//最小生成树不能形成回路
#include<queue>//边的数量等于点的数量减一,即 m = n - 1
using namespace std;//和迪杰斯特拉一样,贪心思想,选出最小值后两边之和一定大于第三边
const int INF=0x3f3f3f3f,N=5e5+1,M=5e5+1;
struct node {
	int  to,next,w;
	friend bool operator < (const node &f1, const node &f2) {
		return f1.w > f2.w;
	}
} edge[M<<1];//无向边要乘2,这是个细节,建议NM直接开大点 
inline int read() { //快读  若在与同一行有string的情况下使用,把下面的readS也写上 如果变long long,记得把s前的int也变了
	char ch=getchar();
	int s=0,w=1;//long long s=0,w=1;
	while(ch<'0' || ch>'9') {
		if(ch=='-')	w*=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') { //不要写成|| !
		s=ch-'0'+(s<<3)+(s<<1);//最大易错点!s=而不是s+=
		ch=getchar();//不要忘记!
	}//如果这个数字后面有回车,这个循环会接收回车,所以不需要在额外来个getchar()
	return s*w;
}
int n,m,cnt,s,t,from,to,w;
int head[N],dis[N];//dis含义是其他未被访问的节点与已被访问的节点所形成的树的最小距离
bool vis[N];
priority_queue<node> Q;
inline void addedge(int from, int to, int w) {
	edge[cnt].to = to;
	edge[cnt].next = head[from];
	edge[cnt].w = w;
	head[from] = cnt++;
}
int num,ans;//num为树上点的数量,要达到n,ans为最小边权和
inline void init() {
	cnt = 0;
	num=0,ans=0;
	memset(head, -1, sizeof(head));
	memset(edge, 0, sizeof(edge));
	memset(dis, 0x3f, sizeof(dis));
}
void prim(int s) { //s为生成树的根节点
	node z;
	z.to=s,z.next=0,z.w=0;//我们真正需要的是他的to当起点,这里把源点赋予to扔队列里启动算法
	Q.push(z);
	dis[s]=0;//根节点到自己距离为0
	while (Q.size()&&num<=n) {//num==n说明树已建成
		z = Q.top();//z.to才是需要用的,因为z是整条边,边的起点已经在树上了,边的终点z.to不在树上
		Q.pop();
		if(vis[z.to])	continue;//无向图也要加上,一个点只能挂到树上一次!
		vis[z.to]=true;//给树加点 这个点已在树上
		num++;//找到一个点
		ans+=z.w;//累加边权
		int start = z.to;
		for (int i = head[start]; ~i; i = edge[i].next) {//经过for循环,更新邻接点到树的最短距离,被更新的边扔进队列
			int end = edge[i].to;
			if (dis[end] > edge[i].w) {//edge[i]这条边起点在树上,所以w边长就是树到边末端点的距离了
				dis[end] = edge[i].w;
				Q.push(edge[i]);//把根节点和这个点组成的边扔进队列,优先队列第一位是离树最短的边
			}
		}
	}
	if(num==n)//图连通
		printf("%d\n",ans);//输出最小边权
	else
		printf("orz\n");//图不连通
}
int main() {
	n=read(),m=read();
	init();
	for (int i = 0; i < m; i++) {
		from=read(), to=read(), w=read() ;
		addedge(from, to, w);
		addedge(to, from, w);//无向图的话请加上
	}
	prim(1);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值