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;
}
一般情况:是要把边-双连通分量合并成一个点 , 然后用所有的桥组成一个新图 , 这样得到的新图就是一棵树了
方法:所有点只属于一个双连通分量 , 所以,对于求边-双连通分量,我们只要把所有桥都标记 , 然后再用dfs来遍历这图并且不经过桥,那么我们得到的就是一个边-双连通分量,根据这个原理 , 我们用并查集来标记两个点是不是属于同一个边-双连通分量,如果当前这条边时桥,那么我们就不标记他们,否则我们就把这两个点所属的树合并成一颗树。
代码:
#include
#include
#include
using namespace std;
#define maxn 110
#define min(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()
{
}
int find(int x)
{
}
void Union(int x , int y)
{
}
int dfs(int u)
{
}
int main()
{
}
2、把点-双连通分量合并
方法:对于点-双连通分量来说 , 每个割点都是属于多个点-双连通分量,对于这种情况,我们就应该通过记录边来确定连通分量,因为每条边只属于一个点-连通分量,所以我们就用一个stack来存储遍历到的边 , 每当我们碰到割点时,就把这些边上的所有点存储。
代码:
//对于求割点 , 有两种标记的方法:1、标记边已经走过的边 ; 2、记录每个点的父亲节点,来判断当前不能被遍历的点是不是父亲节点
int dfs(int u , int fa)
{
}