zoukankan      html  css  js  c++  java
  • POJ3177 Redundant Paths 图的边双连通分量

    题目大意:问一个图至少加多少边能使该图的边双连通分量成为它本身。

    图的边双连通分量为极大的不存在割边的子图。图的边双连通分量之间由割边连接。求法如下:

    1. 求出图的割边
    2. 在每个边双连通分量内Dfs,标记每个节点所属于的双连通分量编号
    3. 构建一新图Tree,一个节点代表一个双连通分量。原图中遍历割边,将割边连接的两个双连通分量在Tree中的对应节点连接。
    4. Tree中算出每个节点的度数,如果一节点度数为1,则其为叶子节点。输出(叶子节点数+1/2)。(连接了叶子节点,就形成了环,Tree中不连接叶子节点的边因为在环内,所以不再是割边了。)

    注意:如果一个边是割边,则其反向边也是割边。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <cassert>
    using namespace std;
    
    #define LOOP(i, n) for(int i=1; i<=n; i++)
    const int MAX_NODE = 5010, MAX_EDGE = 10010 * 2;
    
    struct G {
    	struct Node;
    	struct Edge;
    
    	struct Node {
    		int Id, DfsN, Low, InBlock, Degree;
    		Edge *Head;
    	}_nodes[MAX_NODE], *Root;
    
    	struct Edge {
    		bool IsCut;
    		Node *From, *To;
    		Edge *Next, *Rev;
    		Edge(){}
    		Edge(Node *from, Node *to, Edge *next):From(from),To(to),Next(next),IsCut(false){}
    	}*_edges[MAX_EDGE];
    	
    	int _vCount, _eCount, DfsCnt, BlockCnt, LeafCnt;
    
    	void Init() {
    		memset(_nodes, 0, sizeof(_nodes));
    		_vCount = _eCount = DfsCnt = LeafCnt = 0;
    		BlockCnt = 0;
    	}
    
    	Edge *NewEdge() {
    		_eCount++;
    		return _edges[_eCount] ? _edges[_eCount] : _edges[_eCount] = new Edge();
    	}
    
    	Edge *AddEdge(Node *from, Node *to) {
    		Edge *e = NewEdge();
    		*e = Edge(from, to, from->Head);
    		from->Head = e;
    		return e;
    	}
    
    	void Build(int uId, int vId, bool is2d) {
    		while (_vCount < uId || _vCount < vId)
    			_vCount++;
    		Node *u = uId + _nodes, *v = vId + _nodes;
    		u->Id = uId;
    		v->Id = vId;
    		Edge *e1 = AddEdge(u, v);
    		if (is2d) {
    			Edge *e2 = AddEdge(v, u);
    			e1->Rev = e2;
    			e2->Rev = e1;
    		}
    	}
    
    	void FindCutEdge(Node *u, Edge *Prev) {//易忘点:prev
    		if (u->DfsN)
    			return;
    		u->DfsN = u->Low = ++DfsCnt;
    		for (Edge *e = u->Head; e; e = e->Next) {
    			if (!e->To->DfsN) {
    				FindCutEdge(e->To, e);
    				u->Low = min(u->Low, e->To->Low);
    				if (u->DfsN < e->To->Low)
    					e->IsCut = e->Rev->IsCut = true;//易忘点:e->Rev->IsCut
    			}
    			else if (e->Rev != Prev)
    				u->Low = min(u->Low, e->To->DfsN);
    		}
    	}
    
    	void FindCutEdge() {
    		LOOP(i, _vCount) {//易忘点:图不一定连通,所以要循环。
    			Root = i + _nodes;
    			FindCutEdge(Root, NULL);
    		}
    	}
    
    	void SetBlock(Node *u) {
    		u->InBlock = BlockCnt;
    		for (Edge *e = u->Head; e; e = e->Next)
    			if (!e->IsCut && !e->To->InBlock)
    				SetBlock(e->To);
    	}
    
    	void SetBlock() {
    		LOOP(i, _vCount) {
    			if (!_nodes[i].InBlock) {
    				BlockCnt++;
    				SetBlock(i + _nodes);
    			}
    		}
    	}
    	void SetLeafCnt() {//此处比较有技巧,注意看看
    		LOOP(i, _eCount)
    			_edges[i]->To->Degree++;
    		LOOP(i, _vCount)
    			if (_nodes[i].Degree <= 1)
    				LeafCnt++;
    	}
    }Org, Tree;
    
    int main() {
    #ifdef _DEBUG
    	freopen("c:\noi\source\input.txt", "r", stdin);
    	//freopen("c:\noi\source\output.txt", "w", stdout);
    #endif
    	Org.Init();
    	Tree.Init();
    	int totNode, totEdge, uId, vId;
    	scanf("%d%d", &totNode, &totEdge);
    	LOOP(i, totEdge) {
    		scanf("%d%d", &uId, &vId);
    		Org.Build(uId, vId, true);
    	}
    	Org.FindCutEdge();
    	Org.SetBlock();
    	LOOP(i, Org._eCount)
    		if (Org._edges[i]->IsCut)
    			Tree.Build(Org._edges[i]->From->InBlock, Org._edges[i]->To->InBlock, false);
    	Tree.SetLeafCnt();
    	printf("%d
    ", (Tree.LeafCnt + 1) / 2);
    	return 0;
    }
    

      

  • 相关阅读:
    Dockfile编写常见命令
    coreDNS域名无法解析问题
    开源软件
    容器相关
    日常工具
    持续集成
    mysql主从同步
    生产环境MySQL5.7-my.cnf 配置文件 for linux (转)
    工作交接流程引导图
    文章记录
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8468965.html
Copyright © 2011-2022 走看看