luogu3366 【模板】 最小生成树 Prim

题目大意

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

概念

对于一个无向图,要求选出一些边,使得图上的每一个节点互相连通,且边权和最小。选出的边与节点形成的子图必然是颗树,这棵树叫做最小生成树。

Prim算法

原理

最小生成树中,除根节点外,每一个节点作为一个to节点与它相邻的边的边权(以后简称最小相邻边权)必然是最小的。

实现方法

邻接表

像Dijkstra一样,用一个priority_queue维护已访问的边,使得堆顶的边的边权是最小的。每次循环给出一条边cur,如果cur->to节点不在树中,则cur的权值便是cur->to的最小相邻边权。于是将cur->to节点纳入树中并记录结果。然后,由to节点扩展与它相邻的边e(e->to也不在树内)。

邻接矩阵

对于稠密图,用邻接表的方式还要维护一个堆,时间太慢。所以定义LowLen[u]为u节点目前搜索到的作为一个to节点与它相邻的边的边权的最小值。每次关于树内边的个数的cnt循环,将堆顶出来一条边改为枚举每一个不在树内的节点,找出LowLen[u]最小的u,此时的LowLen[u]便是u最小相邻边权。于是将u纳入树中,记录结果,然后通过u刷新与它相邻的树外节点的LowLen。

#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <cmath>
//#define test
#define LOOP(a, b) for(int a=1;(a)<=(b);a++)
using namespace std;

const int MAX_NODE = 5010, MAX_EDGE = 200010 * 2, INF = 0x3f3f3f3f;

struct Prim
{
	struct Node;
	struct Edge;

	struct Node
	{
		Edge *Head;
		int Id;
		bool Vis;
		
		Node() 
		{ 
			Head = NULL; 
			Vis = false;
			Id = 0;
		}
	}_nodes[MAX_NODE];
	int _vCount;
	Node *Start;

	struct Edge
	{
		Node *From, *To;
		Edge *Next;
		int Len, Id;
		bool InGraph;
		
		Edge()
		{
			From = To = NULL;
			Next = NULL;
			Len = Id = 0;
			InGraph = false;
		}

		bool operator <(const Edge a)const
		{
			return Len > a.Len;
		}
	}_edges[MAX_EDGE];
	int _eCount;

	void Init(int vCount, int sId)
	{
		memset(_nodes, 0, sizeof(_nodes));
		memset(_edges, 0, sizeof(_edges));
		_vCount = vCount;
		_eCount = 0;
		Start = sId + _nodes;
	}

	Edge *NewEdge()
	{
		return ++_eCount + _edges;
	}

	void AddEdge(Node *from, Node *to, int len)
	{
		Edge *e = NewEdge();
		e->Id = _eCount;
		e->From = from;
		e->To = to;
		e->Len = len;
		e->Next = e->From->Head;
		e->From->Head = e;
	}

	void Build(int uId, int vId, int len)
	{
		Node *u = uId + _nodes, *v = vId + _nodes;
		u->Id = uId;
		v->Id = vId;
		AddEdge(u, v, len);
		AddEdge(v, u, len);
	}

	int Proceed()
	{
		int ans = 0, cnt = 0;
		priority_queue<Edge> q;
		Start->Vis = true;//易忘点
		cnt++;//易忘点
		for (Edge *e = Start->Head; e; e = e->Next)
			q.push(*e);
		while (!q.empty() && cnt<_vCount)//易忘点:小于
		{
			Edge temp = q.top();
			q.pop();
			Edge *cur = temp.Id + _edges;
			if (cur->To->Vis)
				continue;
			cur->To->Vis = true;
			cur->InGraph = true;
			ans += cur->Len;
			cnt++;
			for (Edge *e = cur->To->Head; e; e = e->Next)
				if (!e->To->Vis)
					q.push(*e);
		}
		return cnt == _vCount ? ans : -1;
	}
}g;

struct PrimMatrix
{
	int Len[MAX_NODE][MAX_NODE];
	bool InTree[MAX_NODE];
	int LowLen[MAX_NODE];
	int _vCount;

	void Init(int vCount)
	{
		memset(Len, INF, sizeof(Len));
		_vCount = vCount;
	}

	void Build(int u, int v, int dist)
	{
		Len[u][v] = Len[v][u] = min(Len[u][v], dist);
	}

	int Proceed()
	{
		int cnt = 1, ans = 0;
		memset(InTree, false, sizeof(InTree));
		memset(LowLen, INF, sizeof(LowLen));
		InTree[1] = true;//易忘点
		LOOP(v, _vCount)
			LowLen[v] = Len[1][v];
		LOOP(i, _vCount)
		{
			int u, lowLen = INF;
			LOOP(j, _vCount)
			{
				if (!InTree[j] && LowLen[j] < lowLen)
				{
					lowLen = LowLen[j];
					u = j;
				}
			}
			if (lowLen == INF)
				break;
			cnt++;
			ans += lowLen;
			InTree[u] = true;
			LOOP(v, _vCount)//注意从此往后就不用lowLen了。lowLen就是为了确定u用的。
				if (!InTree[v] && Len[u][v] < LowLen[v])
					LowLen[v] = Len[u][v];
		}
		return cnt == _vCount ? ans : -1;
	}
}g1;


int main()

{
	int totNode, totEdge, uId, vId, len;
	scanf("%d%d", &totNode, &totEdge);
	g1.Init(totNode);
	while (totEdge--)
	{
		scanf("%d%d%d", &uId, &vId, &len);
		g1.Build(uId, vId, len);
	}
	int ans = g1.Proceed();
	if (ans == -1)
		printf("orz
");
	else
		printf("%d
", ans);
	return 0;
}

  

原文地址:https://www.cnblogs.com/headboy2002/p/8680951.html