历时好几天,终于完工了!
支持无向图四种功能:
1.割点的求解
2.割边的求解
3.点双连通分量的求解
4.边双连通分量的求解
全部支持重边!!!!全部支持重边!!!!全部支持重边!!!!
测试数据:
10 11
1 5
3 5
4 5
2 4
2 3
4 6
6 8
6 7
7 8
8 10
8 9
/* By:ZUFE_ZZT 该模板经过多次修改与研究,修正了很多错误,增加了很多功能。 无向图,完全支持重边!!完全支持重边!! 【功能如下】 1.求割点的编号,以及去掉割点有多少连通分量 2.求点双连通分量 3.求割边的编号 4.求边双连通分量 */ #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<algorithm> using namespace std; const int maxn=10000+10; //结点数量 const int Maxn=2*100000+10; //边的数量 int low[maxn]; int dfn[maxn]; int U[maxn],V[maxn];//存初始边 int flag[maxn];//判断第i条边是不是割边 int iscut[maxn];//判断i结点是不是割点,去掉之后有几个连通分量 struct Edge { int from,to,id,ans;//ans为1,表示这条边是割边 } edge[Maxn]; vector<int>G[maxn];//邻接表 int N,M;//N个结点,M条边 int tmpdfn;//时间戳 int tot; int son; int Start,End; //以下是输出点双连通分量用的 int top; struct Printf_Egde { int u,v,id; void output() {printf("(%d,%d) ",u,v);} }; Printf_Egde Stack[Maxn]; int Flag[Maxn]; int TxT[maxn];//求边双连通分量用的 void init() { for(int i=0; i<maxn; i++) G[i].clear(); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(iscut,0,sizeof(iscut)); memset(Flag,0,sizeof(Flag)); memset(flag,0,sizeof(flag)); memset(TxT,0,sizeof(TxT)); low[1]=dfn[1]=1; tmpdfn=0; tot=0; son=0; top=-1; } void AddEdge(int u,int v) { edge[tot].from=u; edge[tot].to=v; edge[tot].id=tot; edge[tot].ans=0; G[u].push_back(tot); tot++; edge[tot].from=v; edge[tot].to=u; edge[tot].id=tot; edge[tot].ans=0; G[v].push_back(tot); tot++; } int Tarjan(int u,int id) { tmpdfn++; int lowu=dfn[u]=tmpdfn; for(int i=0; i<G[u].size(); i++) { int B=G[u][i]; Printf_Egde t; if(!Flag[edge[B].id/2])//没有入过栈 { Flag[edge[B].id/2]=1; t.u=u; t.v=edge[B].to; t.id=edge[B].id/2; Stack[++top]=t; } if(!dfn[edge[B].to]) { int lowv=Tarjan(edge[B].to,edge[B].id); lowu=min(lowu,lowv); if(lowv>=dfn[u]) { if(u!=1) iscut[u]++; if(u==1) son++; //输出点双连通分量 printf("点双连通分量:"); while(1) { if(top==-1) break; Printf_Egde t1; t1=Stack[top]; t1.output(); top--; if(t1.id==t.id) break; } printf(" "); //判断是不是割边 if(lowv>dfn[u]) edge[B].ans=1; } } else if(dfn[edge[B].to]) { if(edge[B].id/2==id/2) continue; lowu=min(lowu,dfn[edge[B].to]); } } low[u]=lowu; return lowu; } void Display_Cutting_edge() { for(int i=0; i<2*M; i++) if(edge[i].ans) printf("第%d条边是割边:(%d,%d) ",edge[i].id/2,edge[i].from,edge[i].to); } void Display_Cutting_point() { if(son>1) iscut[1]=son-1; for(int i=Start;i<=End;i++) if(iscut[i]) printf("编号为%d的结点是割点,删除后有%d个连通分量 ",i,iscut[i]+1); } void Dfs(int x,int y) { int XZ=0; for(int i=0;i<G[x].size();i++) { int B=G[x][i]; if(!flag[edge[B].id/2]) { XZ=1; flag[edge[B].id/2]=1; TxT[edge[B].to]=1; printf("(%d,%d) ",edge[B].from,edge[B].to); Dfs(edge[B].to,y+1); } } if(!XZ&&!y) printf("(%d) ",x); } void Slove() { //把桥都标为1 for(int i=0; i<2*M; i++) if(edge[i].ans) flag[edge[i].id/2]=1; for(int i=Start;i<=End;i++) { if(!TxT[i]) { TxT[i]=1; printf("边双连通分量:"); Dfs(i,0); printf(" "); } } } int main() { scanf("%d%d",&N,&M); init(); for(int i=0; i<M; i++) { scanf("%d%d",&U[i],&V[i]); AddEdge(U[i],V[i]); } //设置结点编号的起点和终点 Start=1; End=N; Tarjan(1,-1); //割点的输出 Display_Cutting_point(); //割边的输出 Display_Cutting_edge(); //点双连通分量在Tarjan过程中已经输出了 //求边双连通分量,并输出 Slove(); return 0; }