poj_2186 强连通分支

题目大意

有N头牛,他们中间有些牛会认为另外一些牛“厉害”,且这种认为会传递,即若牛A认为牛B“厉害”,牛B认为牛C“厉害”,那么牛A也认为牛C“厉害”。现给出一些牛的数对(x, y)表示牛x认为牛y厉害。那么,求出所有的牛都认为该牛“厉害”的牛的个数。

题目分析

牛之间的关系,形成一个有向图。其中存在一些强连通分支,若强连通分支内的一个牛被所有牛认为“厉害”,那么整个强连通分支内的牛都被认为“厉害”。因此,将强连通分支合并为一个点,对图重构。 
    重构后的图为一个简单的有向图,题目转换为寻找能从所有点均可达的点的数目(实际数目为点代表的强连通分支内的点数目之和)。使用定理有向无环图中出度为0的点,可以从任何出度不为0的点到达。 
    因此,寻找该有向无环图中出度为0的点的个数,若出度为0的点的个数大于1,则这些出度为0的点之间互相不可达,则不存在所有点均可达的点;若出度为0的点的个数为1,则该出度为0的点代表的强连通分支内点的个数,即为题目的结果。

实现(c++)

#include<stdio.h>
#include<string.h>
#include<vector>
#include<stack>
#include<algorithm>

using namespace std;
#define MAX_NODE 10005
#define min(a, b) a < b? a:b

vector<int> gGraph[MAX_NODE];
stack<int> gStack;
bool gVisited[MAX_NODE];	//判断点是否被访问过
bool gInStack[MAX_NODE];	//判断点是否在栈中
int gDfn[MAX_NODE];			//在DFS过程中,点第一次被访问到的时间
int gLow[MAX_NODE];			//点x下方的点所能到达的序号最小的点的序号
int gIndex;

int gClusterIndex;
int gClusterOfNode[MAX_NODE];	//每个点所属的强连通分支序号

//强连通分支结构体
struct Cluster{
	int cluster_id;
	int node_num;
	vector<int> linked_cluster;
	Cluster(int id, int num) :cluster_id(id), node_num(num){};
	bool LinkedCluster(int cluster){
		return find(linked_cluster.begin(), linked_cluster.end(), cluster) != linked_cluster.end();
	}
	void LinkCluster(int cluster){
		linked_cluster.push_back(cluster);
	}
	~Cluster(){
		linked_cluster.clear();
	}
};

vector<Cluster> gClusters;
//tarjan 算法求强连通分支
void Tarjan(int u){
	gDfn[u] = gLow[u] = ++gIndex;
	gVisited[u] = true;
	gInStack[u] = true;
	gStack.push(u);
	for (int i = 0; i < gGraph[u].size(); i++){
		int v = gGraph[u][i];
		if (gVisited[v] == false){
			Tarjan(v);
			gLow[u] = min(gLow[u], gLow[v]);
		}
		else if(gInStack[v]){ //注意,需要v在栈中才可以
			gLow[u] = min(gLow[u], gDfn[v]);
		}
	}
	if (gDfn[u] == gLow[u]){
		int v, num = 0;
		do{
			v = gStack.top();
			gClusterOfNode[v] = gClusterIndex;
			gStack.pop();
			gInStack[v] = false;		//注意恢复
			num++;
		} while (u != v);
		gClusters.push_back(Cluster(gClusterIndex, num));

		gClusterIndex++;
	}
}

//将强连通分支的各个点染色之后,再重新建图
void ReconstructGraph(int n){
	for (int u = 1; u <= n; u++){
		for (int j = 0; j < gGraph[u].size(); j++){
			int v = gGraph[u][j];
			int uc = gClusterOfNode[u];
			int vc = gClusterOfNode[v];
			if (uc != vc && !gClusters[uc].LinkedCluster(vc))
					gClusters[uc].LinkCluster(vc);
		}
	}
}
/*
int gRoot[MAX_NODE];
int GetRoot(int c){
	if (gRoot[c] != c){
		gRoot[c] = GetRoot(gRoot[c]);
	}
	return gRoot[c];
}
void Union(int c1, int c2){
	int p1 = GetRoot(c1);
	int p2 = GetRoot(c2);
	if (p1 != p2){
		gRoot[p1] = p2;
	}
}

bool DAG(){ //判断一个图是否为连通图,在此题中,可以不用判断
	int n = gClusters.size();

	for (int i = 0; i < n; i++){
		gRoot[i] = i;
	}

	for (int u = 0; u < n; u++){
		for (int i = 0; i < gClusters[u].linked_cluster.size(); i++){
			int v = gClusters[u].linked_cluster[i];
			Union(u, v);
		}
	}
	int r = GetRoot(0);
	for (int i = 1; i < n; i ++){
		if (r != GetRoot(i)){
			return false;
		}
	}
	return true;
}
*/
int main(){
	int n, m, u, v;
	while (scanf("%d %d", &n, &m) != EOF){
		for (int i = 0; i <= n; i++){
			gGraph[i].clear();
		}
		for (int i = 0; i < m; i++){
			scanf("%d %d", &u, &v);
			gGraph[u].push_back(v);
		}
		gIndex = 0;
		gClusterIndex = 0;
		memset(gInStack, false, sizeof(gInStack));
		memset(gVisited, false, sizeof(gVisited));
		gClusters.clear();

		//Tarjan 求强连通分支
		for (int i = 1; i <= n; i++){
			if (!gVisited[i]){
				Tarjan(i);
			}
		}

		//重新构图
		ReconstructGraph(n);

		/*
		if (!DAG()){
			printf("0\n");
			continue;
		}
		*/
		int zero_outdegree_cluster = 0, result = 0;
		for (int i = 0; i < gClusterIndex; i++){
			if (gClusters[i].linked_cluster.empty()){
				zero_outdegree_cluster++;
				result = gClusters[i].node_num;
			}
		}
		//若重构后的图中各个点不能构成一个连通图(将有向边变为无向边之后仍不能),那么就不存在一个点可以被其他所有点可达
		//而此时,图中也肯定存在多于1个点,其出度为0.
		//故,只需要判断重构后的图中,出度为0的点是否为1个即可。若出度为0的点有且只有一个,则返回该“点”(实际为一个强连通分支)
		//中的点的数目,否则,返回0

		if (zero_outdegree_cluster > 1){
			result = 0;
		}
		printf("%d\n", result);
	}
	return 0;
}
时间: 2024-11-18 05:34:03

poj_2186 强连通分支的相关文章

有向图强连通分支的Tarjan算法讲解 + HDU 1269 连通图 Tarjan 结题报告

题目很简单就拿着这道题简单说说 有向图强连通分支的Tarjan算法 有向图强连通分支的Tarjan算法伪代码如下:void Tarjan(u) {dfn[u]=low[u]=++index//进行DFS,每发现一个新的点就对这个点打上时间戳,所以先找到的点时间戳越早,dfn[U]表示最早发现u的时间,low[u]表示u能到达的最早的时间戳.stack.push(u)//将U压入栈中for each (u, v) in E {if (v is not visted)//如果V点没有经历过DFS,则

poj_2553 强连通分支&amp;出度为0的点

题目大意 N个点的有向图中,定义“好点”为: 从该点v出发可以到达的所有点u,均有一条路径使得u可达v. 求出图中所有的“好点”,并按照顺序从小到大输出出来. 题目分析 图存在多个强连通分支,强连通分支内的所有点的行为可以视为一个点的行为:若强连通分支可以到达其他强连通分支,则该强连通分支内的所有点均可以到达其他分支:若强连通分支可以被其他点到达,则该强连通分支内的所有点均可以被其他点到达.因此,将图的强连通分支缩成一个点是一个经常会进行的操作.     将强连通分支缩成一个点之后,形成一个有向

算法系列笔记6(有关图的算法一—搜索,拓扑排序和强连通分支)

简单概念:对于图G(V,E),通常有两种存储的数据结构,一种是邻接矩阵,此时所需要的存储空间为O(V^2):第二种是邻接表,所需要的存储空间为O(V+E).邻接表表示法存在很强的适应性,但是也有潜在的不足,当要快速的确定图中边(u,v)是否存在,只能在顶点u的邻接表中搜索v,没有更快的方法,此时就可以使用邻接矩阵,但要以占用更多的存储空间作为代价:此外当图不是加权的,采用邻接矩阵存储还有一个优势:在存储邻接矩阵的每个元素时,可以只用一个二进位,而不必用一个字的空间. 图的搜索算法 搜索一个图示有

Kosaraju 算法查找强连通分支

有向图 G = (V, E) 的一个强连通分支(SCC:Strongly Connected Components)是一个最大的顶点集合 C,C 是 V 的子集,对于 C 中的每一对顶点 u 和 v,有 u --> v 和 v --> u,亦即,顶点 u 和 v 是互相可达的. 实际上,强连通分支 SCC 将有向图分割为多个内部强连通的子图.如下图中,整个图不是强连通的,但可以被分割成 3 个强连通分支. 通过 Kosaraju 算法,可以在 O(V+E) 运行时间内找到所有的强连通分支.Ko

POJ 2186 -- Popular Cows【强连通分支 &amp;&amp; Tarjan缩点】

Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 27198   Accepted: 10963 Description Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M &

poj_1236 强连通分支

题目大意 有N个学校,这些学校之间用一些单向边连接,若学校A连接到学校B(B不一定连接到A),那么给学校A发一套软件,则学校B也可以获得.现给出学校之间的连接关系,求出至少给几个学校分发软件,才能使得所有的学校均可以获得软件:以及,至少需要添加几条单向边连接学校,才能使得给这些学校中任何一所发软件,其余的学校均可以收到. 题目分析 在一个图中,强连通分支内的任何一个点被“发软件”,则分支内的所有点均可以获得,因此首先求出强连通分支,将强连通分支合并为一点来看.     重构之后的图若只有一个点,

Popular Cows//强连通分支Kosaraju加缩点

题目: Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 38415   Accepted: 15658 Description Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <=

迷宫城堡//强连通分支Tarjan

题目: 迷宫城堡 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 20588    Accepted Submission(s): 8962 Problem Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道

有向强连通分支Tarjan算法

本文转载自:http://blog.csdn.net/xinghongduo/article/details/6195337 说到以Tarjan命名的算法,我们经常提到的有3个,其中就包括本文所介绍的求强连通分量的Tarjan算法.而提出此算法的普林斯顿大学的Robert E Tarjan教授也是1986年的图灵奖获得者(具体原因请看本博“历届图灵奖得主”一文). 首先明确几个概念. 强连通图.在一个强连通图中,任意两个点都通过一定路径互相连通.比如图一是一个强连通图,而图二不是.因为没有一条路