poj_1236 强连通分支

题目大意

有N个学校,这些学校之间用一些单向边连接,若学校A连接到学校B(B不一定连接到A),那么给学校A发一套软件,则学校B也可以获得。现给出学校之间的连接关系,求出至少给几个学校分发软件,才能使得所有的学校均可以获得软件;以及,至少需要添加几条单向边连接学校,才能使得给这些学校中任何一所发软件,其余的学校均可以收到。

题目分析

在一个图中,强连通分支内的任何一个点被“发软件”,则分支内的所有点均可以获得,因此首先求出强连通分支,将强连通分支合并为一点来看。 
    重构之后的图若只有一个点,则只需要向任何一所学校发送即可。即结果为1(至少向1所学校发布软件) 0(不需要添加新边来使得整个图连通). 
    重构之后的图若有多个点,则考虑这些点中入度为0的点:入度为0的点不能被其他点到达,而一个入度不为0的点可以从某个入度为0的点到达,那么只需要向这些入度为0的点分发软件,就可以使得所有的点均能获得软件。 
    重构之后的图中有出度为0的点,在图中,入度为0的点(设为m个)无法从其他点到达,那么为了使得所有的点连通,需要m条路径连接到这m个入度为0的点;而出度为0的点(设为n个)无法到达其他点,那么为了使得所有的点连通,需要n条路径从这n个出度为0的点连出。于是,至少需要添加 max(m, n)条边,使得图中所有的点的入度和出度不为0. 
    同时,在一个有向无环图中,如果该图的所有点均可连接到一块,且每个点的出度和入度均不为0,则该图肯定强连通。于是,结果为 max(m,n)

实现(c++)

#include<stdio.h>
#include<string.h>
#include<vector>
#include<stack>
#include<set>
#define MAX_NODE 105
#define min(a, b) a < b? a:b
#define max(a, b) a > b? a:b
using namespace std;
vector<vector<int> > gGraph;	//存储图结构
stack<int> gStack;				//用于Tarjan算法

bool gVisited[MAX_NODE];		//判断每个点是否被访问过
bool gInStack[MAX_NODE];		//在Tarjan算法中判断点是否在栈中
int gDfn[MAX_NODE];				//Tarjan算法中标记每个点最开始被访问的时间
int gLow[MAX_NODE];				//Tarjan算法中标记从每个点开始的DFS过程中经过的点所能访问到的点的最小的开始时间

int gClusterOfNode[MAX_NODE];	//标记每个点所在的强连通分支
int gIndex;						//标记点被第一次访问的时间
int gClusterIndex;				//强连通分支的编号

//Tarjan算法求强连通分支
void Tarjan(int u){
	gLow[u] = gDfn[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]){
			Tarjan(v);
			gLow[u] = min(gLow[u], gLow[v]);
		}
		else if (gInStack[v]){
			gLow[u] = min(gLow[u], gDfn[v]);
		}
	}
	if (gLow[u] == gDfn[u]){
		int v;
		do{
			v = gStack.top();
			gStack.pop();
			gInStack[v] = false;
			gClusterOfNode[v] = gClusterIndex;	//标记所属的强连通分支编号
		} while (v != u);
		gClusterIndex++;
	}
}
vector<set<int> > gLinkTo;		//将强连通分支缩成点之后,每个点所连接到的另外的点,用set 使得插入时不重复。用于统计出度
vector<set<int> > gLinkFrom;	//将强连通分支缩成点之后,每个点被其他的点连接,用set 使得插入时不重复。用于统计入度

//将强连通分支缩成点,然后统计每个强连通分支的入度和出度
void ReconstructGraph(int nodes, int clusters){
	gLinkTo.resize(clusters);
	gLinkFrom.resize(clusters);

	for (int u = 1; u <= nodes; u++){
		for (int i = 0; i < gGraph[u].size(); i++){
			int v = gGraph[u][i];
			int uc = gClusterOfNode[u];
			int vc = gClusterOfNode[v];
			if (uc != vc){	//注意!!!自身不能连接到自身!
				gLinkTo[uc].insert(vc);
				gLinkFrom[vc].insert(uc);
			}
		}
	}
}

int main(){
	int n, u, v;
	scanf("%d", &n);
	gGraph.resize(n + 1);
	for (int i = 1; i <= n; i++){
		while (scanf("%d", &v) && v != 0){
			gGraph[i].push_back(v);
		}
	}
	gIndex = gClusterIndex = 0;
	memset(gVisited, false, sizeof(gVisited));
	memset(gInStack, false, sizeof(gInStack));
	for (int u = 1; u <= n; u++){
		if (!gVisited[u]){
			Tarjan(u);
		}
	}
	ReconstructGraph(n, gClusterIndex);
	int zero_outdegree = 0, zero_indegree = 0;
	for (int i = 0; i < gClusterIndex; i++){
		if (gLinkFrom[i].empty()){
			zero_indegree++;
		}
		if (gLinkTo[i].empty()){
			zero_outdegree++;
		}
	}
	if (gClusterIndex == 1){	//如果只有一个强连通分支,则直接输出1 0
		printf("1\n0\n");
	}else
		//最少需要发送的版本个数,等于将强连通分支缩成点之后的图中 入度为0的点的个数,
		//因为,一个有向无环图中,所有入度不为0的点,一定可以从某个入度为0的点出发可达。
		//而这些入度为0的点之间不能可达,故至少需要将所有这些入度为0的点设为起点

		//需要连接多少条有向边才能使得整个图称为一个强连通图
		//入度为0的点设为m个,不能被其他任何点可达,因此,要有 m条边连接到 这m个入度为0的点
		//出度为0的点设为n个,无法到达其他点,因此要有n条边从这n个出度为0的点连接出去
		//可以证明,在一个有向无环图中,所有点的入度和出度均不为0,则该图为强连通的
		//则,至少有 max(m, n)条边。

		printf("%d\n%d\n", zero_indegree, max(zero_outdegree, zero_indegree));
	return 0;
}
时间: 2024-11-09 00:43:03

poj_1236 强连通分支的相关文章

有向图强连通分支的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_2186 强连通分支

题目大意 有N头牛,他们中间有些牛会认为另外一些牛“厉害”,且这种认为会传递,即若牛A认为牛B“厉害”,牛B认为牛C“厉害”,那么牛A也认为牛C“厉害”.现给出一些牛的数对(x, y)表示牛x认为牛y厉害.那么,求出所有的牛都认为该牛“厉害”的牛的个数. 题目分析 牛之间的关系,形成一个有向图.其中存在一些强连通分支,若强连通分支内的一个牛被所有牛认为“厉害”,那么整个强连通分支内的牛都被认为“厉害”.因此,将强连通分支合并为一个点,对图重构.     重构后的图为一个简单的有向图,题目转换为寻

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 &

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年的图灵奖获得者(具体原因请看本博“历届图灵奖得主”一文). 首先明确几个概念. 强连通图.在一个强连通图中,任意两个点都通过一定路径互相连通.比如图一是一个强连通图,而图二不是.因为没有一条路