https://blog.csdn.net/a_forever_dream/article/details/103019013?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5
找出所有的桥出来,然后去掉桥即可
#include<cstdio> #include<iostream> using namespace std; const int MAXN=1e5,MAXM=1e6; struct Edge{ int from,to,nxt; }e[MAXM]; int head[MAXN],edgeCnt=1; void addEdge(int u,int v) { e[++edgeCnt].from=u; e[edgeCnt].to=v; e[edgeCnt].nxt=head[u]; head[u]=edgeCnt; } int dfn[MAXN],low[MAXN],dfnCnt=0; bool bridge[MAXM]; void tarjan(int x,int in_edge) { dfn[x]=low[x]=++dfnCnt; for(int i=head[x];i;i=e[i].nxt) { int nowV=e[i].to; if(!dfn[nowV]) { tarjan(nowV,i); if(low[nowV]>dfn[x]) { bridge[i]=bridge[i^1]=1; } low[x]=min(low[x],low[nowV]); } else if(i!=(in_edge^1)) { low[x]=min(low[x],dfn[nowV]); } } } int inDcc[MAXN]; void dfs(int x,int nowDcc) { inDcc[x]=nowDcc;//将X点归入nowdcc这个边双中 for(int i=head[x];i;i=e[i].nxt) { int nowV=e[i].to; if(inDcc[nowV]||bridge[i]) //如果已归到某个边双,或第I条边是桥的话 continue; dfs(nowV,nowDcc); } } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); addEdge(u,v); addEdge(v,u); } for(int i=1;i<=n;i++) //找出所有的桥出来 if(!dfn[i]) tarjan(i,0); int nowDcc=0; for(int i=1;i<=n;i++) //如果i点还没有归到某个边双 if(!inDcc[i]) dfs(i,++nowDcc); printf("%d ",nowDcc); return 0; }
做法1
在请出Tarjan之前,我们先介绍另外一种做法:第一次 dfs找出割边,然后第二次 dfs 在不经过割边的情况下遍历所有点,每一次遍历经过的一个子图就是一个边双。
做法2
用类似找点双的做法,但是栈里面压点,不压边。
int dfn[maxn],low[maxn],belong[maxn],id=0,cnt=0; int zhan[maxn],t=0; void dfs(int x,int from) { dfn[x]=low[x]=++id;zhan[++t]=x; for(int i=first[x];i;i=e[i].next) { if(i==(from^1))continue; int y=e[i].y; if(!dfn[y]) { dfs(y,i); if(low[y]<low[x])low[x]=low[y]; } else if(dfn[y]<low[x])low[x]=dfn[y]; } if(dfn[x]==low[x]) { cnt++; int xx; do{ xx=zhan[t--]; belong[xx]=cnt; }while(xx!=x); } } 原文链接:https://blog.csdn.net/a_forever_dream/java/article/details/103019013