题意
给出一个连通的无向图,求至少增加多少条边让这个图变成边双连通图
思路
其实题目意思很清晰很明白,但是怎么操作才是重点。
那么首先先想想怎么把整个图简化一下, 先求一次边双连通分量。
因为边双连通分量本来就是个边双连通图,那么这里面无论怎样增边,都是无意义的,那么就可以把它缩成一个点。
将所有的边双连通分量都缩成一个点之后,原图变成了一棵树。
那么,如何让一棵树变成一个双连通图,是一个问题。
在网上很容易找到要增加的边就等于叶子的数量加一的和除以二,这里设叶子的数量为
nleaves
,那么答案就是
(nleaves+1)÷2
怎么证明是个问题啊……
其实很容易想到,在一棵树上面,选取到根节点最远的两个叶子节点,然后它们之间连一条边,就可以发现这里产生了一个环,这个环是双连通的。容易发现,如果换成其他的加边的方法, 都没有这种方法产生的环所含的节点多(或一样多),所以这样做一定是最优的。
然后把这个环缩成一个点,然后把这个点作为原图的根节点,然后反复操作,最终得到的答案就是加了
(nleaves+1)÷2
条边
但是为什么叶子的个数总是为1<-_<-
,有待思考<-_<-
,或者哪个大神来解答都行<-_<-
代码
关于代码,其实是有些和之前求点双连通分量是不太一样的。
因为可以用更加简单的方式去求。
首先先找到原图中所有的桥,然后桥把原图分成了许多双连通分量,即去掉桥之后,剩下的连通分量刚好对应原图的双连通分量
找到桥之后做dfs,标记点,注意别经过桥就可以了
然后找叶子这一块,其实缩点之后是可以不用考虑的,直接把所属的双连通分量的标号拿来就用
然后统计每一个子节点的度数,因为是无向图嘛,所以总度数为2的点就只连了一条边,也就是叶子节点了。
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int MAXN = 5010,
MAXM = 20010;
struct Edge {
int u, v, ne;
} e[MAXM];
int head[MAXN];
int n, m;
int m_cnt;
int bcc_cnt, pre[MAXN], low[MAXN], dfs_clock, bccno[MAXN];
bool is_bridge[MAXM];
int d[MAXN];
int dfs(int u, int fa) {
int lowu = pre[u] = ++dfs_clock;
for(int i = head[u]; ~i; i = e[i].ne) {
int v = e[i].v;
if(!pre[v]) {
int lowv = dfs(v, u);
lowu = min(lowu, lowv);
if(lowv > pre[u]) {
is_bridge[i] = 1;
is_bridge[i ^ 1] = 1;
}
} else {
if(pre[v] < pre[u] && v != fa) {
lowu = min(lowu, pre[v]);
}
}
}
low[u] = lowu;
return lowu;
}
void dfs2(int u) {
if(bccno[u]) {
return ;
}
bccno[u] = bcc_cnt;
for(int i = head[u]; ~i; i = e[i].ne) {
if(!is_bridge[i]) {
dfs2(e[i].v);
}
}
}
void find_bcc(int n) {
memset(pre, 0, sizeof pre);
memset(is_bridge, 0, sizeof is_bridge);
dfs_clock = 0;
for(int i = 1; i <= n; ++i) {
if(!pre[i]) {
dfs(i, -1);
}
}
memset(bccno, 0, sizeof bccno);
bcc_cnt = 1;
for(int i = 1; i <= n; ++i) {
if(!bccno[i]) {
dfs2(i);
++bcc_cnt;
}
}
}
void AddEdge(int u, int v) {
e[m_cnt].u = u;
e[m_cnt].v = v;
e[m_cnt].ne = head[u];
head[u] = m_cnt++;
}
void init() {
memset(head, -1, sizeof head);
m_cnt = 0;
}
int main(void) {
init();
scanf("%d%d", &n, &m);
for(int i = 0; i < m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
AddEdge(u, v);
AddEdge(v, u);
}
find_bcc(n);
for(int i = 1; i <= n; ++i) {
for(int j = head[i]; ~j; j = e[j].ne) {
if(bccno[i] != bccno[e[j].v]) {
d[bccno[i]]++;
d[bccno[e[j].v]]++;
}
}
}
int cnt = 0;
for(int i = 1; i < bcc_cnt; ++i) {
if(d[i] == 2) {
cnt++;
}
}
printf("%d\n", (cnt + 1) >> 1);
return 0;
}