题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4612
给一个无向图, 加上一条边后,求桥最少有几个;
那我们加的那条边的两个顶点u,v;一定是u,v之间含有桥的数量最多,然后uv之间的桥都没了,剩下的就是要求的结果;
树的直径的定义刚好就是两个节点之间含有最多的边;
下面是有关树的直径的知识;
这个题目需要手动扩展,不然会爆栈,而且手动扩展的话要用C++提交。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<stdio.h> #include<string.h> #include<queue> #include<algorithm> using namespace std; #define N 200005 int Head1[N], Head2[N], cnt[3]; int Stack[N], top, dfn[N], low[N], Time, n, m; int nBridge, Bridge[N]; int dist[N], vis[N], Max, index, Is[N]; struct Edge { int v, next; } e1[10*N], e2[10*N]; void Init() { top = nBridge = Time = Max = index = 0; cnt[1] = cnt[0] = 0; memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); memset(Bridge, 0, sizeof(Bridge)); memset(dist, 0, sizeof(dist)); memset(Stack, 0, sizeof(Stack)); memset(Is, 0, sizeof(Is)); memset(Head1, -1, sizeof(Head1)); memset(Head2, -1, sizeof(Head2)); } void Add(Edge e[],int Head[], int u, int v, int k) { e[cnt[k]].v = v; e[cnt[k]].next = Head[u]; Head[u] = cnt[k]++; } void Tarjar(int u, int father) { low[u] = dfn[u] = ++Time; Stack[top++] = u; Is[u] = 1; int v, k=0; for(int i=Head1[u]; i!=-1; i=e1[i].next) { v = e1[i].v; if(v==father && !k)///避免重边; { k++; continue; } if(!dfn[v]) { Tarjar(v, u); low[u] = min(low[u], low[v]); } else low[u] = min(low[u], dfn[v]); } if(low[u] == dfn[u]) { nBridge++;///可以代表缩点后的节点个数; while(1) { v = Stack[--top]; Is[v] = 0; Bridge[v] = nBridge;///缩点; if(u==v) break; } } } void bfs(int s) { queue<int>Q; int p, q; memset(vis, 0, sizeof(vis)); vis[s] = 1; dist[s] = 0; Q.push(s); while(!Q.empty()) { p = Q.front(); Q.pop(); for(int i=Head2[p]; i!=-1; i=e2[i].next) { q = e2[i].v; if(!vis[q]) { vis[q] = 1; dist[q] = dist[p] + 1; Q.push(q); if(Max<dist[q]) { Max = dist[q]; index = q; } } } } } int main() { int u, v; while(scanf("%d%d", &n, &m), m + n)///输入时由于m n弄反了,TLE的我想哭; { Init(); for(int i=1; i<=m; i++) { scanf("%d%d", &u, &v); Add(e1, Head1, u, v, 0); Add(e1, Head1, v, u, 0);///原来的树; } Tarjar(1, 0); for(int i=1; i<=n; i++) { for(int j=Head1[i]; j!=-1; j=e1[j].next) { int u = Bridge[i]; int v = Bridge[e1[j].v]; if(u != v ) { Add(e2, Head2, u, v, 1); Add(e2, Head2, v, u, 1);///缩点后的树; } } } bfs(1); bfs(index);///求树的直径的过程; printf("%d ", nBridge-1-Max);///缩点后形成的树每条边都是桥;所以总桥的个数为节点数-1; } return 0; }
一年后又来写了一下,比不用扩栈也可以;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<stdio.h> #include<string.h> #include<algorithm> #include<vector> #include<queue> #include<string> #include<stack> #include<map> using namespace std; #define N 205010 #define INF 0x3f3f3f3f #define met(a, b) memset(a, b, sizeof(a)) vector<vector<int> >G; vector<vector<int> >G1; int low[N], dfn[N], Time; int Block[N], nblock; int IsStack[N], Sta[N], top; int n, m, nbridge; void Init() { met(low, 0); met(dfn, 0); met(Block, 0); met(IsStack, 0); met(Sta, 0); G.clear(); G.resize(n+1); G1.clear(); G1.resize(n+1); Time = nblock = top = nbridge = 0; } void Tarjan(int u, int fa) { low[u] = dfn[u] = ++Time; IsStack[u] = 1; Sta[top++] = u; int len = G[u].size(), v, k = 0; for(int i=0; i<len; i++) { v = G[u][i]; if(v == fa && !k) { k++; continue; } if(!dfn[v]) { Tarjan(v, u); low[u] = min(low[u], low[v]); if(low[v] > dfn[u]) nbridge ++; } else if(IsStack[v]) { low[u] = min(low[u], dfn[v]); } } if(low[u] == dfn[u]) { ++nblock; do { v = Sta[--top]; IsStack[v] = 1; Block[v] = nblock; }while(u != v); } } int vis[N], Max, Index, dist[N]; void bfs(int s) { met(vis, 0); met(dist, 0); vis[s] = 1; queue<int> Q; Q.push(s); while(Q.size()) { int p = Q.front();Q.pop(); int len = G1[p].size(), q; for(int i=0; i<len; i++) { q = G1[p][i]; if(!vis[q] && dist[q] < dist[p]+1) { dist[q] = dist[p]+1; if(dist[q] > Max) { Max = dist[q]; Index = q; } vis[q] = 1; Q.push(q); } } } } int main() { while(scanf("%d %d", &n, &m), m+n) { Init(); int u, v; for(int i=1; i<=m; i++) { scanf("%d %d", &u, &v); G[u].push_back(v); G[v].push_back(u); } Tarjan(1, 0); for(int i=1; i<=n; i++) { int len = G[i].size(), v, u = Block[i]; for(int j=0; j<len; j++) { v = Block[G[i][j]]; if(u != v) { G1[u].push_back(v); G1[v].push_back(u); } } } Max = 0, Index = -1; bfs(1); bfs(Index); ///printf("%d %d ", nbridge, Max); printf("%d ", nbridge-Max);///或者nblock-1-Max; } return 0; }