关于无向图中连通分量缩点的问题

1、把边-双连通分量合并

一般情况:是要把边-双连通分量合并成一个点 , 然后用所有的桥组成一个新图 , 这样得到的新图就是一棵树了

方法:所有点只属于一个双连通分量 , 所以,对于求边-双连通分量,我们只要把所有桥都标记 , 然后再用dfs来遍历这图并且不经过桥,那么我们得到的就是一个边-双连通分量,根据这个原理 , 我们用并查集来标记两个点是不是属于同一个边-双连通分量,如果当前这条边时桥,那么我们就不标记他们,否则我们就把这两个点所属的树合并成一颗树。

代码:
#include
#include
#include
using namespace std;

#define maxn 110
#define min(x , y)  (x)<(y)?(x):(y)
int grap[maxn][maxn] , low[maxn];
int pre[maxn] , edge[maxn*maxn][2];
int n , m;
int dfs_clock , cnt;
int p[maxn];

void init()
{
      memset(grap , 0 , sizeof(grap));
      memset(pre , 0 , sizeof(pre));
      dfs_clock = cnt = 0;
      for(int i = 1; i <= n; i++)
            p[i] = i;
}

int find(int x)
{
      int g = x , h;
      while(p[x] != x)  x = p[x];

      while(p[g] != x)
      {
            h = p[g];
            p[g] = x;
            g = h;
      }

      return x;
}

void Union(int x , int y)
{
      int g = find(x) , h = find(y);
      if(g != h)  p[g] = h;
}

int dfs(int u)
{
      int lowu = low[u] = ++dfs_clock;
      int i , lowv;
      for(i = 1; i <= n; i++)
            if(grap[u][i])
            {
                  grap[u][i] -= 1;
                  grap[i][u] -= 1;
                  if(!pre[i])
                  {
                        pre[i] = 1;
                        lowv = dfs(i);
                        lowu = min(lowv , lowu);
                        if(lowv > low[u]) //记录桥
                              edge[cnt][0] = u , edge[cnt++][1] = i;
                        else Union(u , i);
                  }
                  else if(lowu > low[i])
                        lowu = low[i];
            }

      return lowu;     
}

int main()
{
      int i , j , x , y;
      for(i = 1; i <= n; i++)
            if(!pre[i])
            {
                  pre[i] = 1;
                  dfs(i);
            }
      return 0;
}

2、把点-双连通分量合并

方法:对于点-双连通分量来说 , 每个割点都是属于多个点-双连通分量,对于这种情况,我们就应该通过记录边来确定连通分量,因为每条边只属于一个点-连通分量,所以我们就用一个stack来存储遍历到的边 , 每当我们碰到割点时,就把这些边上的所有点存储。

代码:
//对于求割点 , 有两种标记的方法:1、标记边已经走过的边 ; 2、记录每个点的父亲节点,来判断当前不能被遍历的点是不是父亲节点
int dfs(int u , int fa)
{
      int lowu = low[u] = ++dfs_clock;
      int i , lowv;
      for(i = head[u]; i != -1; i = edge[i].next)
      {
            int v = edge[i].u;
            gh.u = u;
            gh.next = v;
            if(!pre[v])
            {
                  xy.push(gh);
                  pre[v] = 1;
                  lowv = dfs(v , u);
                  lowu = min(lowv , lowu);
                  if(lowv >= low[u])
                  {
                        //cout<<u<<endl;
                        dfs_node++;
                        new_edge[dfs_node].clear();
                        for(;;)
                        {
                              node cf = xy.top();  xy.pop();
                              if(p[cf.u] != dfs_node)  
                              {
                                    p[cf.u] = dfs_node ;
                                    new_edge[dfs_node].push_back(cf.u);
                              }
                              if(p[cf.next] != dfs_node)
                              {
                                    p[cf.next] = dfs_node ;
                                    new_edge[dfs_node].push_back(cf.next);
                              }
                              if(cf.u == u && cf.next == v)  break;
                        }
                  }
            }
            else if(v != fa && low[u] > low[v])
            {
                  lowu = min(lowu , low[v]) ; //这里这条边,可记录 , 也可不记录
                  xy.push(gh);
            }
      }

      return lowu;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值