题意:最少添加几条边使得整个图变为边双联通分量。
求出途中所有的桥,删除之。将剩下的边连通分量缩成一个点(此处可用并查集实现)。然后用桥将这些点连通,设图中度为一的点的个数为n,则(n+1)>>1,即为答案。
边连通度:是一个原本连通的子图变成不连通所需要删除的最少的边数。
桥:删除一条边使得原本连通的图变的不连通,则称此边为桥。
边双连通分量:边连通分量大于等于二的子图称为边双连通分量。
无向连通图中割点与桥的求法。
任取一点为起点开始DFS,将DFS过程中访问的边取出来组成一棵树,则在这棵树中有:
对于根节点(即选定的DFS起点)若有多棵子树(>= 2)则其为割点。
对于非根节点 f 有:若 f 存在一个子节点 u 满足其与其子孙均没有连回 f 的祖先的边(连回 f 的不计),则 f 亦为割点。
若 f (判断桥时 f 可以为根节点)存在一个子节点 u 满足其与其子孙均没有连回 u 的祖先的边(连回 f 的不计),则边(f,u)即为桥。
判断点 u 是否有指向 f 的祖先的方法:
设 w 为 u 与其子孙中的任意一点,v为与 w 直接相连的一点。
1. 若 v 未进行DFS,则记录 v 的深度depth[v] 并对于进行DFS。
2. 若 v 已进行DFS且在等待回溯,则 比较depth[v] 与 depth[f] 的大小,若depth[v] < depth[f] 即 f 比 v 深,则说明存在一条指向 f 的祖先的边。
若所有与 w 直接相连的 v 均不存在第二种情况,则说明 f 为割点。
对于所有 已进行DFS且在等待回溯 的 v , 若有Min(depth[v]) >= depth[u] ,则边(f,u)即为桥。
#include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <queue> #include <cmath> #include <algorithm> #define LL long long #define PI (acos(-1.0)) #define EPS (1e-10) using namespace std; const int MAXN = 1010; int Top_Edge,Top_Bridge; struct N { int v,next; }Edge[2*MAXN]; struct E { int u,v; }bridge[2*MAXN]; int head[MAXN]; int mv[MAXN]; int depth[MAXN]; int degree[MAXN]; int father[MAXN]; bool Is_Cut[MAXN]; void link(int u,int v) { Edge[++Top_Edge].v = v; Edge[Top_Edge].next = head[u]; head[u] = Top_Edge; } int Find(int x) { while(x != father[x]) { x = father[x]; } return x; } void Merge(int u,int v) { int fu = Find(u); int fv = Find(v); if(fu != fv) father[fu] = fv; } int dfs(int s,int f,int h) { mv[s] = 1;//表示灰色 depth[s] = h; int p,Min_Depth = MAXN,Temp_Depth,Sum_Son = 0; for(p = head[s];p != -1; p = Edge[p].next) { if(Edge[p].v != f) { if(mv[Edge[p].v] == 0) { Sum_Son++; Temp_Depth = dfs(Edge[p].v,s,h+1); if(Temp_Depth < Min_Depth) Min_Depth = Temp_Depth; if(Temp_Depth >= depth[s]) { Is_Cut[s] = true; } } else if(mv[Edge[p].v] == 1) { Temp_Depth = depth[Edge[p].v]; if(Temp_Depth < Min_Depth) Min_Depth = Temp_Depth; } } } mv[s] = 2;//表示黑色 if(Sum_Son == 1 && h == 1) { Is_Cut[1] = false; } if(Min_Depth >= depth[s] && f != -1) { bridge[Top_Bridge].u = s; bridge[Top_Bridge++].v = f; } else if(f != -1) { Merge(s,f); } return Min_Depth; } int main() { int n,m,i,u,v; while(scanf("%d %d",&n,&m)!=EOF) { Top_Edge = -1; Top_Bridge = 0; memset(head,-1,sizeof(int)*(n+2)); memset(degree,0,sizeof(int)*(n+2)); memset(mv,0,sizeof(int)*(n+2));//表示白色 memset(Is_Cut,false,sizeof(bool)*(n*2)); for(i = 0;i < m; ++i) { scanf("%d %d",&u,&v); link(u,v); link(v,u); } for(i = 1;i <= n; ++i) { father[i] = i; } dfs(1,-1,1); for(i = 0;i < Top_Bridge; ++i) { degree[Find(bridge[i].u)]++; degree[Find(bridge[i].v)]++; } int ans = 0; for(i = 1;i <= n; ++i) { if(degree[i] == 1) { ans++; } } printf("%d\n",((ans+1)>>1)); } return 0; }