双联通图:就是一个图中任意两点之间都有两条不重合的路径相连。
桥:指的是强联通分量之间的边。
将一个无向图变成一个双联通图所需的最小边为:
首先将该图缩点,缩完点之后的图就是一个树,设该树的叶子节点为x;
所需边数:(x + 1)/ 2;
- int dfn[N], low[N], timestamp;
- int stk[N], id[N], all[N];
- bool in_stk[N];
- int h[N], e[N], ne[N], idx;
- int n, m, top, scc_cnt;
- int du[N];
- bool is_bridge[N];
- inline void add(int a, int b)
- {
- e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
- }
- void tarjan(int u, int from)
- {
- // 记录序号以及初始化可以到达的最小标号点
- dfn[u] = low[u] = ++ timestamp;
- stk[++ top] = u;
- // 便利当前节点的所有节点
- for(int i = h[u]; ~i; i = ne[i])
- {
- int j = e[i];
- if(!dfn[j]) // 该点未遍历
- {
- tarjan(j, i); // 便利子节点
- low[u] = min(low[u], low[j]); // 更新当前节点可以到达的最小序号点
- if(dfn[u] < low[j]) // 表明是一个桥
- is_bridge[i] = is_bridge[i ^ 1] = 1;
- }
- else if(i != (from ^ 1))
- low[u] = min(low[u], low[j]);
- }
- if(dfn[u] == low[u])
- {
- int y;
- ++ scc_cnt;
- do
- {
- y = stk[top --];
- id[y] = scc_cnt;
- }while(y != u);
- }
- }
- inline void sovle()
- {
- cin >> n >> m;
- memset(h, -1, sizeof h);
- while(m --){
- int a, b;
- cin >> a >> b;
- add(a, b);
- add(b, a);
- }
- tarjan(1, -1);
- for(int i = 0; i < idx; i ++)
- if(is_bridge[i]) // 将该点的出度加一
- du[id[e[i]]] ++;
- int s = 0;
- for(int i = 1; i <= scc_cnt; i ++)
- if(du[i] == 1) // 若是该点的出度为1,说明是一个叶子节点
- s ++;
- cout << (s + 1) / 2 << endl;
- }