pku1236 Network of Schools
求强连通分量的基础题,用来练一下刚搞懂的Tarjan:
Tarjan算法的过程就是不断避免把桥纳入强连通分量中
注意到以下性质:
1,桥一定是DFS树中的边
2,一条树边v-w为桥,当且仅当不存在回边将w的一个子孙与w的一个祖先相连
#include <iostream>
#include <stack>
using namespace std;
#define Max(a,b)(a<b?b:a)
#define MAXN 101
int p[MAXN],ecnt,n,c,ideg[MAXN],odeg[MAXN];
int lowlink[MAXN],//结点所处强连通分量的代表结点,也是具有最小时间截的结点(在DFS树中深度最浅)(动态更新)
dfn[MAXN],//访问时间(固定)
sign,//时间截
SCC[MAXN];//结点所处强连通分量
bool visited[MAXN],instack[MAXN];
stack<int> st;
struct Edge{
int v,next;
}edg[10000];
void update(int &a,int b){
a=(a<b?a:b);
}
void dfs(int u){
int i,v;
st.push(u);
visited[u]=instack[u]=true;
lowlink[u]=dfn[u]=++sign;
for(i=p[u];i!=-1;i=edg[i].next){
v=edg[i].v;
if(visited[v]){
if(instack[v]){
update(lowlink[u],bfn[v]);//u-v是返祖边
}
}
else{
dfs(v);
update(lowlink[u],lowlink[v]);
}
}
if(lowlink[u]==dfn[u]){//若u是代表结点,则深度比其深的仍在栈内的待处理结点均属于该强连通分量
c++;
do{
v=st.top();
st.pop();
instack[v]=false;
SCC[v]=c;
}while(v!=u);
}
}
void Tarjan(){
int i;
c=0;
sign=0;
memset(visited,false,sizeof(visited));
memset(instack,false,sizeof(instack));
for(i=1;i<=n;i++)
if(!visited[i])
dfs(i);
}
void solve(){
int i,u,v,ic,oc;
Tarjan();//求强连通分量
memset(ideg,0,sizeof(ideg));
memset(odeg,0,sizeof(odeg));
for(u=1;u<=n;u++){
for(i=p[u];i!=-1;i=edg[i].next){
v=edg[i].v;
if(SCC[u]!=SCC[v]){
odeg[SCC[u]]++;
ideg[SCC[v]]++;
}
}
}
if(c==1){
printf("1\n0\n");
return;
}
ic=oc=0;
for(i=1;i<=c;i++){
if(ideg[i]==0)
ic++;
if(odeg[i]==0)
oc++;
}
printf("%d\n%d\n",ic,Max(ic,oc));
}
int main(){
int u,v;
while(scanf("%d",&n)!=EOF){
while(!st.empty())
st.pop();
ecnt=0;
memset(p,-1,sizeof(p));
for(u=1;u<=n;u++){
while(scanf("%d",&v) && v){
edg[ecnt].v=v;
edg[ecnt].next=p[u];
p[u]=ecnt++;
}
}
solve();
}
return 0;
}
#include <stack>
using namespace std;
#define Max(a,b)(a<b?b:a)
#define MAXN 101
int p[MAXN],ecnt,n,c,ideg[MAXN],odeg[MAXN];
int lowlink[MAXN],//结点所处强连通分量的代表结点,也是具有最小时间截的结点(在DFS树中深度最浅)(动态更新)
dfn[MAXN],//访问时间(固定)
sign,//时间截
SCC[MAXN];//结点所处强连通分量
bool visited[MAXN],instack[MAXN];
stack<int> st;
struct Edge{
int v,next;
}edg[10000];
void update(int &a,int b){
a=(a<b?a:b);
}
void dfs(int u){
int i,v;
st.push(u);
visited[u]=instack[u]=true;
lowlink[u]=dfn[u]=++sign;
for(i=p[u];i!=-1;i=edg[i].next){
v=edg[i].v;
if(visited[v]){
if(instack[v]){
update(lowlink[u],bfn[v]);//u-v是返祖边
}
}
else{
dfs(v);
update(lowlink[u],lowlink[v]);
}
}
if(lowlink[u]==dfn[u]){//若u是代表结点,则深度比其深的仍在栈内的待处理结点均属于该强连通分量
c++;
do{
v=st.top();
st.pop();
instack[v]=false;
SCC[v]=c;
}while(v!=u);
}
}
void Tarjan(){
int i;
c=0;
sign=0;
memset(visited,false,sizeof(visited));
memset(instack,false,sizeof(instack));
for(i=1;i<=n;i++)
if(!visited[i])
dfs(i);
}
void solve(){
int i,u,v,ic,oc;
Tarjan();//求强连通分量
memset(ideg,0,sizeof(ideg));
memset(odeg,0,sizeof(odeg));
for(u=1;u<=n;u++){
for(i=p[u];i!=-1;i=edg[i].next){
v=edg[i].v;
if(SCC[u]!=SCC[v]){
odeg[SCC[u]]++;
ideg[SCC[v]]++;
}
}
}
if(c==1){
printf("1\n0\n");
return;
}
ic=oc=0;
for(i=1;i<=c;i++){
if(ideg[i]==0)
ic++;
if(odeg[i]==0)
oc++;
}
printf("%d\n%d\n",ic,Max(ic,oc));
}
int main(){
int u,v;
while(scanf("%d",&n)!=EOF){
while(!st.empty())
st.pop();
ecnt=0;
memset(p,-1,sizeof(p));
for(u=1;u<=n;u++){
while(scanf("%d",&v) && v){
edg[ecnt].v=v;
edg[ecnt].next=p[u];
p[u]=ecnt++;
}
}
solve();
}
return 0;
}