kosaraju算法
以一个城堡问题为例:
hdu1269
题意:
为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。
输入输出:
输入包含多组数据,输入的第一行有两个数:N和M,接下来的M行每行有两个数a和b,表示了一条通道可以从A房间来到B房间。文件最后以两个0结束。
对于输入的每组数据,如果任意两个房间都是相互连接的,输出"Yes",否则输出"No"。
案例
3 3
1 2
2 3
3 1
3 3
1 2
2 3
3 2
0 0
思路:
ks算法是比较简单的dfs方法,很容易实现,加油!
①首先要得到一个反转边的有向图
②用反转边的图,从1结点到n结点进行dfs搜索(算法模块为rdfs),后序遍历得到一个数组!
③我们需要用到的是逆后序遍历数组,这个逆后序数组其实就是一个伪拓扑排序
④按照逆后序遍历数组对所有结点进行dfs搜索,结点访问过则进行标记
⑤每进行一次dfs,则就找到一个强联通分量!
#include <iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
using namespace std;
vector<int> g[10005];
vector<int> rg[10005];
vector<int> vec;
int vis[10005];
int n;
void rdfs(int v)
{
vis[v]=1;
for(int i=0; i<rg[v].size(); i++)
{
if(!vis[rg[v][i]])
{
rdfs(rg[v][i]);
}
}
vec.push_back(v);
}
int cnt;
void dfs(int v)
{
vis[v]=1;
for(int i=0; i<g[v].size(); i++)
{
if(!vis[g[v][i]])
{
dfs(g[v][i]);
}
}
}
int main()
{
int m;
while(cin>>n>>m)
{
if(n==m&&m==0)
break;
for(int i=1; i<=n; i++)
g[i].clear(),rg[i].clear();
int a,b;
for(int i=0; i<m; i++)
{
cin>>a>>b;
g[a].push_back(b);
rg[b].push_back(a);
}
vec.clear();
memset(vis,0,sizeof(vis));
for(int i=1; i<=n; i++)
if(!vis[i])
rdfs(i);
cnt=0;
memset(vis,0,sizeof(vis));
for(int i=n-1; i>=0; i--)
{
if(!vis[vec[i]])
dfs(vec[i]),cnt++;
}
if(cnt==1)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}
Tarjan算法
poj1236
题目:N个学校,给出N个学校网络的相同关系,我们希望所有学校收到发送的消息,每个学校收到时会共享给自己单向相通的学校。问,一,至少发几个消息让所有学校都收到,二,需要构建几条单向边,让这些学校变成强连通。
思路:把强连通的学校变成一个点,得到一个DAG,在DAG上求出度入度,入度为0点的个数就是1题答案,第二题稍微有点点绕,自己画个图屡屡就清楚了,出度为0的入度为0的点的个数中取最大就是。
需要一条特判:万一得到点只有一个,说明所有学校已经强连通,则不需要加边(入出度为0皆是1)
#include <iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
#define maxn 100005
vector<int> tu[maxn];
int dfn[maxn],low[maxn],id[maxn],in[maxn],out[maxn];
int zu[maxn];
int ans=0;
int color=0;
stack<int> cun;
int n,m;
void dfs(int x)
{
cun.push(x);
dfn[x]=low[x]=++ans;
zu[x]=1;
int v;
for(int i=0; i<tu[x].size(); i++)
{
v=tu[x][i];
if(!dfn[v])
{
dfs(v);
low[x]=min(low[x],low[v]);
}
else if(zu[v])
{
low[x]=min(low[x],low[v]);
}
}
int tmp;
if(low[x]==dfn[x])
{
color++;
while(cun.top()!=x)
{
id[cun.top()]=color;
zu[cun.top()]=0;
cun.pop();
}
zu[cun.top()]=0;
id[cun.top()]=color;
cun.pop();
}
}
void tarjan()
{
memset(dfn,0,sizeof(dfn));
while(!cun.empty())
cun.pop();
memset(low,0,sizeof(low));
for(int i=1; i<=n; i++)
{
if(!dfn[i])
{
dfs(i);
}
}
for(int i=1; i<=n; i++)
{
for(int j=0; j<tu[i].size(); j++)
{
if(id[i]!=id[tu[i][j]])
{
in[id[tu[i][j]]]++;
out[id[i]]++;
}
}
}
if(color==1)
{
cout<<1<<endl;
cout<<0;
return ;
}
int tmp1=0,tmp2=0;
for(int i=1; i<=color; i++)
{
if(in[i]==0)
tmp1++;
if(out[i]==0)
tmp2++;
}
cout<<tmp1<<endl;
cout<<max(tmp2,tmp1);
}
int main()
{
int x;
cin>>n;
for(int i=1; i<=n; i++)
{
tu[i].clear();
while(cin>>x&&x!=0)
{
tu[i].push_back(x);
}
}
tarjan();
return 0;
}
POJ2186 tarjan+缩点
思路: 把互相认可的牛看成一个点,然后得到一个DAG,判断一下这个DAG有几个连通分量,如果只有一个,说明,有一个点是所有牛都认可的(而且点内部也互相认可),那么这个DAG里出度为0的点,这个点里有几个牛,那么就是答案了
#include <iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
#define maxn 100005
vector<int> tu[maxn];
int dfn[maxn],low[maxn],id[maxn],in[maxn],out[maxn];
int num[maxn];
int zu[maxn];
int ans=0;
int color=0;
stack<int> cun;
int n,m;
vector<int> arr[maxn];
void dfs(int x){
zu[x]=1;
cun.push(x);
dfn[x]=low[x]=++ans;
for(int i=0;i<tu[x].size();i++){
int v= tu[x][i];
if(!dfn[v]){dfs(v);low[x]=min(low[x],low[v]);}else if(zu[v]){low[x]=min(low[x],low[v]);}//深搜找强联通
}
if(dfn[x]==low[x]){ //强连通缩点
color++;
while(cun.top()!=x){
id[cun.top()]=color;
arr[color].push_back(cun.top());
cun.pop();
}id[cun.top()]=color;arr[color].push_back(cun.top());cun.pop();
}
}
int main()
{
cin>>n>>m;
int a,b;
for(int i=0;i<m;i++){
cin>>a>>b;
tu[a].push_back(b);
}
for(int i=1;i<=n;i++){
if(!dfn[i]){
dfs(i);
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<tu[i].size();j++){
if(id[tu[i][j]]!=id[i]){
in[id[tu[i][j]]]++;
out[id[i]]++;
}
}
}
int idx=0;
for(int i=1;i<=color;i++){
if(out[i]==0){
if(idx==0)
idx=i;
else{
idx=-1;
}
}
}
if(idx!=-1&&idx!=0){
cout<<arr[idx].size()<<endl;
}
else {
cout<<0<<endl;
}
return 0;
}