红字部分内容转载自:https://blog.csdn.net/gyarenas/article/details/8712338 博主允许转载,故以此博客为基础浅谈自己对于重边的理解。
何为重边?
顾名思义,对于两个顶点A和B,有不止一条边从A连接到B。
在学习生成树的时候我们知道,树本身不能构成回路,那么对于A到B与A到B这一对重边,对于我们求解其实是没有影响的。
也就是说:有向图的的情况比较简单只有一种强连通,重边和连向自己的边对于强连通都没有任何影响。
然而我们考虑重边往往是对于无向图,这也是最容易出错的地方。
无向图的双连通要分点双连通(biconnected)和边双连通(edge_biconnected)。
点双连通:删除一个点后仍然连通
边双连通:删除一个边后仍然连通
可以脑补一下图形,就能想到:连向自己的边对于俩种双连通也没有任何影响。
接下来这一句话就是思考的主要内容:
但是重边对点双连通没有影响,但是对于边双连通有影响。
为什么重边对点双连通没有影响,因为在删除一个点之后,与它相连的点都会被删除,那么既然会被删除,即使再多重边对它也没有太大的影响。
而重边对于边双连通的影响很大,我举个例子:
因为重边的出现,图的连通性发生了改变,然而这种改变恰巧是颠覆性的!
比如说现在我问的是图中的双连通分支的数量:第一个图是0,第二个图是1.
在求边双连通时,要求对于任意俩点至少存在两条“边不重复”的路径,所以这个时候表示图我们不能用vector了,而是用邻接表,添加边的时候我们要一次添加正反俩条边,而且要相互可以索引查找,类似网络流里的反向弧,这样在我们dfs求割边时要以边的下标作为标记,在访问一了一条边时,要把这条边和其反向边同时标记为访问,最后对所有的边进行遍历,发现low[e.v] < pre[e.u]时,同样要把正反俩条边标记成割边,最后在不经过桥的情况下dfs求出边双连通分量即可
可我们为什么要判断重边呢?
题目中给定边,既然是重边,所以对边不加判断的运算不就好了。
直接对边不加判断的运算?
但直接运算的话,我们可不好知道对于两个点究竟什么时候是单边,什么时候是多边。
而对于两个点之间的单通道,如果我们从一个点指向另一个点,而结果是它所指向的那个点是它自己,就要考虑是否有重边,如果有重边代表当前是双连通分支,我们没必要跳出当前层,反之,我们需要跳出当前层。
1:使用map处理重边:
map<int,int>mapit;
inline bool isHash(int u,int v)
{
if(mapit[u*maxn+v]) return true;
if(mapit[v*maxn+u]) return true;
mapit[u*maxn+v]=mapit[v*maxn+u]=true;
return false;
}
2:使用贪心处理重边:
struct node{
int u,v;
}edge1[maxn];
bool cmp(node a,node b)
{
if(a.u==b.u) return a.v<b.v;;
return a.v<b.u;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;++i)
{
scanf("%d%d",&u,&v);
if(u==v) continue;
if(u>v) swap(u,v);
edge1[i].u=u;
edge1[i].v=v;
}
sort(edge1,edge1+n,cmp);
for(int i=0;i<n;++i)
{
if(i==0||&&edge1[i].u==edge1[i+1].u||edge1[i].v==edge1[i+1].v)
{
if(i<n-1&&edge1[i].u!=edge1[i+1].u&&edge1[i].v!=edge1[i+1].v)
{
addedge(edge1[i].u,edge1[i].v,true);
addedge(edge1[i].v,edge1[i].u,true);
}
else
{
addedge(edge1[i].u,edge1[i].v,false);
addedge(edge1[i].v,edge1[i].u,false);
}
}
}
}
/*
之所以进行这部分操作,是区别开哪部分是重边,哪一部分不是重边。
就用Tarjan算法求桥为例,如果当前操作的边是重边,我们不需要判断是当前点所指向的点是否又指向了子节点,反之需要判断。
*/