<题目链接>
题目大意:
给出一个连通图,问你在这个连通图上加一条边,使该连通图的桥的数量最小,输出最少的桥的数量。
解题分析:
首先,通过Tarjan缩点,将该图缩成一颗树,树上的每个节点都是一个边双连通分量,树上的每条边都是桥,现在需要挑出两个点,将它们直接相连,这样它们原始路径上所有的桥因为形成了环而全部消失,因此为了使剩下的桥最少,我们需要找到路径上桥最多的两点,又由于缩点后,树的每条边都是桥,所以这里就转化为树上距离两点的最远距离,也就是求树的直径。
下面Tarjan的时候需要注意的是,vis记录的是访问过边的编号不是节点的编号。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <queue> 6 using namespace std; 7 #define mem(a,b) memset(a,b,sizeof(a)) 8 typedef pair<int, int> pll; 9 const int N = 2e5+5; 10 const int M = 1e6+5; 11 int num, num1, top, cnum,maxdis, pos,cnt; 12 int head[N], head1[N], dis[N],instk[N], stk[N], dfn[N], low[N], belong[N]; 13 int vis[M<<1]; 14 struct Edge{ 15 int to, next; 16 }e[M<<1], e1[M<<1]; 17 18 void add(int u, int v) { 19 e[num].to = v, e[num].next = head[u], head[u] = num++; 20 } 21 void add1(int u, int v) { 22 e1[num1].to = v, e1[num1].next = head1[u], head1[u] = num1++; 23 } 24 void init() { 25 num = num1 = cnt = cnum = top = 0; 26 mem(head,-1),mem(head1,-1),mem(belong,0),mem(instk,0); 27 mem(vis,0),mem(stk,0),mem(dfn,0),mem(low,0),mem(dis,0); 28 } 29 pll edge[M]; 30 void tarjan(int u){ 31 dfn[u] = low[u] = ++cnt; 32 instk[u] = 1; 33 stk[++top] = u; 34 for(int i = head[u]; ~i; i = e[i].next) { 35 int v = e[i].to; 36 if(vis[i])continue; 37 vis[i] = vis[i^1] = 1; //将正、反两边全部标记 38 if(!dfn[v]) { 39 tarjan(v); 40 low[u] = min(low[u], low[v]); 41 } else if(instk[v]) { 42 low[u] = min(low[u], dfn[v]); 43 } 44 } 45 if(low[u] == dfn[u]) { 46 ++cnum; //cnum为边双连通分量的数量 47 int v; 48 do{ 49 v = stk[top--]; 50 instk[v] = 0; 51 belong[v] = cnum; //将该连通块染色 52 } while(v != u); 53 } 54 } 55 void bfs(int u){ 56 queue <int> q; 57 q.push(u); 58 dis[u] = 0; 59 mem(vis,0); 60 vis[u] = 1; 61 maxdis = 0, pos = u; 62 while(!q.empty()) { 63 int u = q.front(); q.pop(); 64 for(int i = head1[u]; ~i; i = e1[i].next) { 65 int v = e1[i].to; 66 if(vis[v])continue; 67 vis[v] = 1; 68 dis[v] = dis[u]+1; 69 if(dis[v]>maxdis) { //更新树上的最远距离 70 maxdis = dis[v]; 71 pos = v; //并且记录下该点 72 } 73 q.push(v); 74 } 75 } 76 } 77 int main() 78 { 79 int n, m, x, y; 80 while(scanf("%d%d",&n,&m)!=EOF,n||m){ 81 init(); 82 for(int i = 0; i<m; i++) { 83 scanf("%d%d", &x, &y); 84 edge[i].first = x, edge[i].second = y; //记录下所有的边,用结构体记录也可以 85 add(x, y),add(y, x); 86 } 87 tarjan(1); 88 int edgenum = 0; 89 for(int i = 0; i<m; i++){ //将缩点后的所有点建图,跑BFS,求树的直径 90 int x = edge[i].first, y = edge[i].second; 91 if(belong[x]!=belong[y]) { 92 add1(belong[x], belong[y]); 93 add1(belong[y], belong[x]); 94 edgenum++; //桥的数量+1 95 } 96 } 97 bfs(belong[1]); 98 bfs(pos); //求出此时树的直径,即一条路径上所含的最多桥的数量 99 int ans = edgenum-maxdis; 100 printf("%d\n", ans); 101 } 102 }
2018-11-07