zoukankan      html  css  js  c++  java
  • 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
    ");
    			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
    ", result);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Java 多线程(四) 多线程访问成员变量与局部变量
    再一贴[亲爱的,我不小心怀孕了~!]
    寒铁沉香木出处
    时间格式
    测试
    有关裁缝的
    转贴
    各种贝壳产地
    捕捞资料
    矿木资料
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4870241.html
Copyright © 2011-2022 走看看