题目大意
给出一个无向图,求出最小生成树,如果该图不连通,则输出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; }