您的位置:首页 > 其它

强连通分量之Korasju 与tarjan两小模版题

2016-01-16 21:21 399 查看
Korasaju算法求有向图强连通分支

procedure Strongly_Connected_Components(G);

begin

1.深度优先遍历G,算出每个结点u的结束时间f[u],起点如何选择无所谓。

2.深度优先遍历G的转置图GT, 选择遍历的起点时,按照结点的结束时间从大到小进行。遍历的过程中,一边遍历, 一边给结点做分类标记,每找到一个新的起点,分类标记值就加1。

3. 第2步中产生的标记值相同的结点构成深度优先森林中的一棵树,也即一个强连通分量。

end.

poj2186.

题意比较简单就不写了。

解题思路:

1. 求出所有强连通分量

2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。

3. DAG上面如果有唯一的出度为0的点,则该点能被所有的点可达。那么该点所代表的连通分量上的所有的原图中的点,都能被原图中的所有点可达,则该连通分量的点数,就是答案。

4. DAG上面如果有不止一个出度为0的点,则这些点互相不可达,原问题无解,答案为0。

缩点的时候不一定要构造新图,只要把不同强连通分量的点染不同颜色,然后考察各种颜色的点有没有连到别的颜色的边即可(即其对应的缩点后的DAG图上的点是否有出边)。

ps:

有向无环图中唯一出度为0的点,一定可以由任何点出发均可达.

(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点)

AC代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<stdio.h>
#include<string.h>
using namespace std;
vector<int>G[10001];
vector<int>rG[10002];
vector<int >vs;
bool used[10001];
int cmp[10001],a,b;
int n,m;
void dfs(int v)
{
used[v]=true;
for(int i=0; i<G[v].size(); i++)
if(!used[G[v][i]])
dfs(G[v][i]);
vs.push_back(v);
}
void rdfs(int v,int k)
{
used[v]=true;
cmp[v]=k;
for(int i=0; i<rG[v].size(); i++)
if(!used[rG[v][i]])rdfs(rG[v][i],k);

}
int scc()
{
memset(used,0,sizeof(used));
vs.clear();
for(int i=0; i<n; i++)
if(!used[i])
dfs(i);
memset(used,0,sizeof(used));
int k=0;
for(int i=vs.size()-1; i>=0; i--)
if(!used[vs[i]])
rdfs(vs[i],k++);
return k;
}

int  sloved()
{
int nn=scc();
int num=0;
int u;
for(int i=0; i<n; i++)
if(cmp[i]==nn-1)
{
u=i;
num++;
}
memset(used,0,sizeof(used));
rdfs(u,0);
for(int i=0;i<n;i++)
{
if(!used[i])
{
num=0;
break;
}
}
return num;
}

int main()
{

scanf("%d%d",&n,&m);
for(int i=0; i<m; i++)
{
scanf("%d%d",&a,&b);
G[a-1].push_back(b-1);
rG[b-1].push_back(a-1);
}
printf("%d\n",sloved());

return 0;
}


poj1236

题目大意:N个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。 2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。

问题可以转化成给定一个有向图,求:

1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点

2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点

ps:

有向无环图中所有入度不为0的点,一定可以由某个入度为0的点出发可达。

(由于无环,所以从任何入度不为0的点往回走,必然终止于一个入度为0的点)

AC代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<stdio.h>
#include<string.h>
#include<stack>
using namespace std;
vector< vector<int> >G;
stack<int>S;
int low[102],dfn[102],fla[102],inde[102],outde[102],cmp[102];
int in,out,cnt,a,b,time,n;
void tarjan(int s)
{
dfn[s]=cnt++;
low[s]=dfn[s];
fla[s]=1;//栈内标记
S.push(s);
for(int i=0; i<G[s].size(); i++)
{
int k=G[s][i];
if(!dfn[k])
{
tarjan(k);
low[s]=min(low[s],low[k]);
}
else if(fla[k]&&low[s]>dfn[k])
low[s]=dfn[k];
}
if(low[s]==dfn[s])
{
time++;
int e;
for(e=S.top(),S.pop(); e!=s; e=S.top(),S.pop())
{
cmp[e]=time;
fla[e]=0;
}
cmp[e]=time;
fla[e]=0;
}
}
int sloved()
{
if(time==1)
{
in=1;
return 0;
}
for(int i=0; i<=n; i++)
for(int j=0; j<G[i].size(); j++)
{
int   k=G[i][j];
if(cmp[i]!=cmp[k])
{
outde[cmp[i]]++;
inde[cmp[k]]++;
}
}
for(int i=1; i<=time; i++)
{
if(inde[i]==0)
in++;
if(outde[i]==0)
out++;
}
return max(in,out);
}
int main()
{

while(scanf("%d",&n)==1)
{
G.clear();
G.resize(n+1);
for(int i=1; i<=n; i++)
while(scanf("%d",&a)&&a)
if(a!=i)
G[i].push_back(a);
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(fla,0,sizeof(fla));
memset(inde,0,sizeof(inde));
memset(outde,0,sizeof(outde));
memset(cmp,0,sizeof(cmp));
cnt=1,time=0,in=0,out=0;
for(int i=1; i<=n; i++)
if(!dfn[i])
tarjan(i);
int  k=sloved();
printf("%d\n%d\n",in,k);

}
return 0;
}


Korasju 和tarjan复杂度都是O(v+e),但是tarjan更常用一点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: