hdu 3836,tarjan算法的应用(有向图缩点)

来源:互联网 发布:自动分析软件 编辑:程序博客网 时间:2024/05/21 18:43
// hdu Equivalent Sets.cpp : 定义控制台应用程序的入口点。///*题目描述:将题目中的集合转换为顶点,A集合是B集合的子集,转换为一条有向边<A,B>,即题目给我们一个有向图,问最少需要添加多少条边使之成为强连通图。解题思路:通过tarjan算法找出图中的所有强连通分支,并将每一个强连通分支缩成一个点(因为强连通分量本身已经满足两两互相可达)。要使缩点后的图成为强连通图,每个顶点最少要有一个入度和一个出度,一条边又提供一个出度和一个入度。所以可以通过统计没有入度的顶点数 noInDegree 和 没有出度的顶点数 noOutDegree。所需要添加的边数就是noInDegree和noOutDegree中的最大值。*/#include "stdafx.h"#include <iostream>#include <vector>#include <stack>using namespace std;const int MAXN = 20000+10;vector<int>grap[MAXN]; //稀疏图,用邻接表表示图stack<int>S;int low[MAXN]; //low[u] 为u或u的子树能够追溯到的最早的栈中节点的次序编号int num[MAXN]; //num[u] 为u搜索的次序编号(时间戳)int visited[MAXN];  //标记是否已经被搜索过int instack[MAXN]; //标记是否在栈中int index; //顶点的前序编号int cnt_scc;   //记录总共将图缩成多少个点int belong[MAXN];  //belong[i] = j 表示原图的点i缩点后为点jint min(int a, int b){return a < b ? a : b;}int max(int a, int b){return a > b ? a : b;}//初始化void init(int n){for(int i=0; i<=n; i++){grap[i].clear();}while(!S.empty()){S.pop();}memset(instack,0,sizeof(instack));memset(visited,0,sizeof(visited));memset(low,-1,sizeof(low));memset(num,-1,sizeof(num));memset(belong,-1,sizeof(belong));index = 0;cnt_scc = 0;}//找出连通分支,并缩点void tarjan(int v){low[v] = num[v] = ++index;S.push(v);instack[v] = 1;visited[v] = 1;for(int i=0; i<grap[v].size(); i++){int w = grap[v][i];if(!visited[w]){tarjan(w);low[v] = min(low[v],low[w]);  //v或v的子树能够追溯到的最早的栈中节点的次序编号}else if(instack[w])  //(v,w)为后向边{low[v] = min(low[v],num[w]);}}int u;if(low[v] == num[v])  //满足强连通分支条件,进行缩点{++cnt_scc;do{u = S.top();belong[u] = cnt_scc;  //缩点S.pop();instack[u] = 0;    //出栈解除标记}while(u != v);}}int main(){int n,m;int a,b;int indegree[MAXN];int outdegree[MAXN];while(cin>>n>>m){init(n);memset(indegree,0,sizeof(indegree));memset(outdegree,0,sizeof(outdegree));while(m--){cin>>a>>b;grap[a].push_back(b);}for(int i=1; i<=n; i++){if(!visited[i]){tarjan(i);}}//求缩点后,各个顶点的出度和入度for(int i=1; i<=n; i++){for(int j=0; j<grap[i].size(); j++){int k = grap[i][j];if(belong[i] != belong[k]){indegree[belong[k]]++;outdegree[belong[i]]++;}}}a = b = 0;for(int i=1; i<=cnt_scc; i++){if(!indegree[i]){a++;}if(!outdegree[i]){b++;}}if(cnt_scc == 1) //如果图已经为强连通图{cout<<"0"<<endl;}else{cout<<max(a,b)<<endl;}}return 0;}