Hoj 1520 The Bottom of a Graph/Poj 2186 Popular Cows/Poj 1904 King's Quest

这几题练习强连通分量的Tarjan求法:

在一个有向图中,如果两个点a,b之间存在a->b的路径以及b->a的路径,则称ab在同一强联通分量(SCCstrongly connected component)之中。因此可以将图划分为几个子图,每一个子图中都是一个极大强联通分量。如果将所有的强连通分量都缩成一个点,原图就变成了一个DAG(有向无环图)。

可以用Tarjan算法、Kosaraju算法、Gabow算法等来求图的强联通分量,其中的Tarjan算法在求图的割点、割边等方面也有很广泛的应用。

Tarjan算法是基于深度优先搜索的,只需要一次深搜和一个栈即可求出有向图的SCC

DFS过程中,每当遍历到一个节点,就将其压栈,同时为每个点都标记颜色,初始所有的点为白色,正在DFS的点为灰色,已经搜索完成的点为黑色。同时用dfn数组记录每个点的时间戳(即搜索的次序)。下面给出low数组的定义:

初始:dfn[u] = low[u],如果有边u->v,则:


第一题:Hoj 1520 The Bottom of a Graph

链接:http://acm.hit.edu.cn/hoj/problem/view?id=1520

求强联通分量后缩点,求出度为0的点。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;

#define Maxn 5005
#define Maxm 50005
struct Edge
{
  int a,b;
}edge[Maxm];

int first[Maxn];
int next[Maxm];
int total;

int sccno[Maxn];
bool instack[Maxn];
int dfn[Maxn];
int low[Maxn];
int dfs_clock;
int scc_cnt;

int in[Maxn],out[Maxn];

stack <int> st;
void addEdge(int a,int b)
{
  total++;
  edge[total].a = a;edge[total].b = b;
  next[total] = first[a];
  first[a] = total;
}
void tarjan(int u)
{
  dfn[u] = low[u] = ++dfs_clock;
  st.push(u);
  instack[u] = true;
  for(int i=first[u];i!=-1;i=next[i])
  {
    int v = edge[i].b;
    if(!dfn[v])
    {
      tarjan(v);
      low[u] = min(low[u],low[v]);
    }
    else if(instack[v])
    {
      low[u] = min(low[u],dfn[v]);
    }
  }
  if(dfn[u] == low[u])
  {
    scc_cnt++;
    while(1)
    {
      int v = st.top();
      st.pop();
      instack[v] = false;
      sccno[v] = scc_cnt;
      if(u == v) break;
    }
  }
}
void find_scc(int n)
{
  scc_cnt = dfs_clock = 0;
  memset(dfn,0,sizeof(dfn));
  memset(low,0,sizeof(low));
  memset(instack,false,sizeof(instack));
  while(!st.empty())
  {
    st.pop();
  }
  for(int i=1;i<=n;i++)
  {
    if(!dfn[i]) tarjan(i);
  }
}
void solve(int n)
{
  find_scc(n);
  for(int i=1;i<=n;i++)
  {
    for(int j=first[i];j!=-1;j=next[j])
    {
      if(sccno[i]!=sccno[edge[j].b])
      {
        out[sccno[i]]++;
        in[sccno[edge[j].b]]++;
      }
    }
  }
  vector <int> temp;
  for(int i=1;i<=n;i++)
  {
    if(out[sccno[i]] == 0) temp.push_back(i);
  }
  for(int i=0;i<temp.size();i++)
  {
    if(i == 0) printf("%d",temp[i]);
    else printf(" %d",temp[i]);
  }
  puts("");
}
void init()
{
  memset(first,-1,sizeof(first));
  memset(next,-1,sizeof(next));
  memset(in,0,sizeof(in));
  memset(out,0,sizeof(out));
  total = 0;
}
int main()
{
  #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
  #endif
    int n,m;
    int a,b;
    while(scanf(" %d",&n)!=EOF && n!=0)
    {
        scanf(" %d",&m);
        init();
        for(int i=0;i<m;i++)
        {
          scanf(" %d %d",&a,&b);
          addEdge(a,b);
        }
        solve(n);
    }
    return 0;
}
第二题:Poj 2186  Popular Cows

题目链接:http://poj.org/problem?id=2186

和上题类似,强连通分量所点后求出度为0的点,但是此题只能有一个出度为0的强联通。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;

#define Maxn 10005
#define Maxm 50005
struct Edge
{
  int a,b;
}edge[Maxm];

int first[Maxn];
int next[Maxm];
int total;

int sccno[Maxn];
bool instack[Maxn];
int dfn[Maxn];
int low[Maxn];
int dfs_clock;
int scc_cnt;

int in[Maxn],out[Maxn];

stack <int> st;
void addEdge(int a,int b)
{
  total++;
  edge[total].a = a;edge[total].b = b;
  next[total] = first[a];
  first[a] = total;
}
void tarjan(int u)
{
  dfn[u] = low[u] = ++dfs_clock;
  st.push(u);
  instack[u] = true;
  for(int i=first[u];i!=-1;i=next[i])
  {
    int v = edge[i].b;
    if(!dfn[v])
    {
      tarjan(v);
      low[u] = min(low[u],low[v]);
    }
    else if(instack[v])
    {
      low[u] = min(low[u],dfn[v]);
    }
  }
  if(dfn[u] == low[u])
  {
    scc_cnt++;
    while(1)
    {
      int v = st.top();
      st.pop();
      instack[v] = false;
      sccno[v] = scc_cnt;
      if(u == v) break;
    }
  }
}
void find_scc(int n)
{
  scc_cnt = dfs_clock = 0;
  memset(dfn,0,sizeof(dfn));
  memset(low,0,sizeof(low));
  memset(instack,false,sizeof(instack));
  while(!st.empty())
  {
    st.pop();
  }
  for(int i=1;i<=n;i++)
  {
    if(!dfn[i]) tarjan(i);
  }
}
int solve(int n)
{
  find_scc(n);
  for(int i=1;i<=n;i++)
  {
    for(int j=first[i];j!=-1;j=next[j])
    {
      if(sccno[i]!=sccno[edge[j].b])
      {
        out[sccno[i]]++;
        in[sccno[edge[j].b]]++;
      }
    }
  }
  int ans = 0;
  int bottomNum = 0;
  int flag = 0;
  for(int i=1;i<=n;i++)
  {
    if(out[sccno[i]] == 0) 
    {
        ans ++;
        if(flag == 0) 
        {
          bottomNum  = sccno[i];
          flag = 1;
        }
        else
        {
            if(bottomNum!=sccno[i]) return 0;
        }
    }
  }
  return ans;
}
void init()
{
  memset(first,-1,sizeof(first));
  memset(next,-1,sizeof(next));
  memset(in,0,sizeof(in));
  memset(out,0,sizeof(out));
  total = 0;
}
int main()
{
  #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
  #endif
    int n,m;
    int a,b;
    while(scanf(" %d %d",&n,&m)!=EOF)
    {
        init();
        for(int i=0;i<m;i++)
        {
          scanf(" %d %d",&a,&b);
          addEdge(a,b);
        }
        int ans = solve(n);
        printf("%d\n", ans);
    }
    return 0;
}
第三题:Poj 1904 King's Quest

题目链接:http://poj.org/problem?id=1904

先把男上向喜欢的女生连线,然后在给出的一组可行解中。女生向对应的男生连线。

求此有向图的强连通分量即可。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;

#define Maxn 4005
#define Maxm 300005
struct Edge
{
  int a,b;
}edge[Maxm];

int first[Maxn];
int next[Maxm];
int total;

int sccno[Maxn];
bool instack[Maxn];
int dfn[Maxn];
int low[Maxn];
int dfs_clock;
int scc_cnt;

stack <int> st;
void addEdge(int a,int b)
{
  total++;
  edge[total].a = a;edge[total].b = b;
  next[total] = first[a];
  first[a] = total;
}
void tarjan(int u)
{
  dfn[u] = low[u] = ++dfs_clock;
  st.push(u);
  instack[u] = true;
  for(int i=first[u];i!=-1;i=next[i])
  {
    int v = edge[i].b;
    if(!dfn[v])
    {
      tarjan(v);
      low[u] = min(low[u],low[v]);
    }
    else if(instack[v])
    {
      low[u] = min(low[u],dfn[v]);
    }
  }
  if(dfn[u] == low[u])
  {
    scc_cnt++;
    while(1)
    {
      int v = st.top();
      st.pop();
      instack[v] = false;
      sccno[v] = scc_cnt;
      if(u == v) break;
    }
  }
}
void find_scc(int n)
{
  scc_cnt = dfs_clock = 0;
  memset(dfn,0,sizeof(dfn));
  memset(low,0,sizeof(low));
  memset(instack,false,sizeof(instack));
  while(!st.empty())
  {
    st.pop();
  }
  for(int i=1;i<=n;i++)
  {
    if(!dfn[i]) tarjan(i);
  }
}
int solve(int n)
{
  find_scc(n);
  return 0;
}
void init()
{
  memset(first,-1,sizeof(first));
  memset(next,-1,sizeof(next));
  total = 0;
}
int main()
{
  #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
  #endif
  int n;
  int num;
  int b;
  init();
  while(scanf(" %d",&n)!=EOF)
  {
    for(int a=1;a<=n;a++)
    {
      scanf(" %d",&num);
      for(int j=0;j<num;j++)
      {
        scanf(" %d",&b);
        addEdge(a,b+n);
      }
    }
    for(int a=1;a<=n;a++)
    {
      scanf(" %d",&b);
      addEdge(b+n,a);
    }
    solve(2*n);
    vector <int> temp;
    for(int i=1;i<=n;i++)
    {
      temp.clear();
      for(int j=first[i];j!=-1;j=next[j])
      {
        int v = edge[j].b;
        if(sccno[i] == sccno[v]) temp.push_back(v-n);
      }
      sort(temp.begin(),temp.end());
      printf("%d",temp.size());
      for(int j=0;j<temp.size();j++)
      {
        printf(" %d",temp[j]);
      }
      puts("");
    }
  }
  return 0;
}



你可能感兴趣的:(Hoj 1520 The Bottom of a Graph/Poj 2186 Popular Cows/Poj 1904 King's Quest)