zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    JQuery Ajax调用asp.net后台方法
    擦亮自己的眼睛去看SQLServer之简单Insert
    擦亮自己的眼睛去看SQLServer之简单Select
    SQL Server CONVERT() 函数
    给reporting services加个条件型的格式 (轉)
    优化SQL语句:in 和not in的替代方案
    技术不如你,但老板就是赏识他,为什么?
    LINQ to SQL活学活用(1):这要打破旧观念(轉)
    [续] MATLAB 混合编程——下篇:调用其它编程语言
    [精] MATLAB 混合编程——上篇:被其它编程语言调用
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8680951.html
Copyright © 2011-2022 走看看