题目大意: 给定一个有向图,求出哪些点之间可以相互达到?最后输出的是,可以相互到达的点的对数
思路:这道题明显是求 强连通分量的个数,假设某个连通分量里面节点数为n,那么该连通分量的对数为 n*(n-1)/2 , 只要把所有连通分量的对数加起来就是整张图的对数
本题关键还是求 强连通分量的个数。我网上查了一下,决定用 Tarjan算法。
Tarjan算法:
核心还是dfs深度遍历;
用数组dfn[i]表示搜索到该节点所需要的时间戳、low[i]表示节点i直接或者间接达到的时间最小的点;
当u入栈,扫描u能够达到的所有节点,如果节点v没有访问过,那就先dfs遍历v, low[u] = min(low[u],low[v]) ,如果v在栈里,low[u] = min(low[u],dfn[v]); 当low[u] = dfn[u] 时,栈内节点u以及u以上所有的点全部出栈,这些点为一个强连通子图的节点。
以上时算法的实现步骤,关于证明,呃。。我也不太明白
#include <iostream> #include <vector> using namespace std; #define MAX 10010 int n; //表示节点数目 vector<int> g[MAX]; //动态数组,节约空间 int Bcnt; //强连通分量个数 int Top; //栈顶 int Index; //时间戳 int low[MAX],dfn[MAX]; //Tarjan算法里的两个重要数组 int belong[MAX],stack[MAX]; //belong数组表示该节点属于哪一个连通分量 bool instack[MAX]; int answer = 0; void Init_tarjan(){ Bcnt = Top = Index = 0; for(int i=1;i<=n;i++) low[i] = dfn[i] = 0; } void Tarjan(int u){ stack[Top++] = u; //将元素u入栈 instack[u] = 1; //标记元素已经入栈 low[u] = dfn[u] = ++Index; for(int i=0;i<g[u].size();i++){ int v = g[u][i]; if(!dfn[v]){ Tarjan(v); low[u] = min(low[v],low[u]); } else if(instack[v]) low[u] = min(low[u],dfn[v]); } if(low[u] == dfn[u]){ Bcnt++; int sum = 0; int v; do{ v = stack[--Top]; //出栈 instack[v] = 0; belong[v] = Bcnt; sum++; //表示连通子图中节点的个数 }while(u!=v); sum = (sum * (sum-1))/2; answer = answer + sum; } } int main(){ int m; //图中的边数 cin>>n>>m; while(m--){ int x,y; cin>>x>>y; g[x].push_back(y); } Init_tarjan(); //先初始化 for(int i=1;i<=n;i++){ if(!dfn[i]) Tarjan(i); } cout<<answer<<endl; return 0; }