题意:一个无向图,问建立一条新边以后桥的最小数量。

  分析:缩点以后,找出新图的树的直径,将这两点连接即可。

  但是题目有个note:两点之间可能有重边!而用普通的vector保存边的话,用v!=fa的话是没办法让重边访问的,因此,使用数组模拟邻接表的方法来储存边。

这样,只要访问了一条边以后,令E[i].vis=E[i^1].vis=1即可,这样可以防止无向图的边和重边搞混。原理就是按位异或,一个奇数^1变为比它小1的偶数,反之亦然:如5^1=4,4^1=5。

  具体见代码:

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <string.h>
  4 #include <stack>
  5 #include <vector>
  6 #include <set>
  7 #include <queue>
  8 //#pragma comment(linker, "/STACK:1024000000,1024000000")
  9 using namespace std;
 10 
 11 const int N = 200000+5;
 12 
 13 int n,m,dfn[N],low[N];
 14 int head[N];
 15 int tot=0;
 16 int dfs_clock;
 17 int bridge;
 18 int belong[N];
 19 int scc_cnt;
 20 int maxd;
 21 bool vis[N];
 22 vector<int> G[N];
 23 stack<int> S;
 24 
 25 struct edge
 26 {
 27     int v;
 28     int vis;
 29     int nxt;
 30 }E[2*1000000+10];
 31 
 32 void addEdge(int u,int v)
 33 {
 34     E[tot].v=v;
 35     E[tot].vis=0;
 36     E[tot].nxt=head[u];
 37     head[u]=tot++;
 38 
 39     E[tot].v=u;
 40     E[tot].vis=0;
 41     E[tot].nxt=head[v];
 42     head[v]=tot++;
 43 }
 44 
 45 void tarjan(int u)
 46 {
 47     dfn[u]=low[u]=++dfs_clock;
 48     S.push(u);
 49     for(int i=head[u];i!=-1;i=E[i].nxt)
 50     {
 51         int v = E[i].v;
 52         if(E[i].vis) continue;
 53         E[i].vis=E[i^1].vis=1;
 54 
 55         if(!dfn[v])
 56         {
 57             tarjan(v);
 58             low[u]=min(low[u],low[v]);
 59 
 60             if(low[v]>dfn[u]) bridge++;
 61         }
 62         else if(!belong[v])
 63         {
 64             low[u]=min(low[u],dfn[v]);
 65         }
 66     }
 67 
 68     if(dfn[u]==low[u])
 69     {
 70         scc_cnt++;
 71         for(;;)
 72         {
 73             int x = S.top();S.pop();
 74             belong[x] = scc_cnt;
 75             if(x==u) break;
 76         }
 77     }
 78 }
 79 
 80 void init()
 81 {
 82     memset(head,-1,sizeof(head));
 83     tot=0;
 84     memset(dfn,0,sizeof(dfn));
 85     dfs_clock=0;
 86     bridge=0;
 87     memset(belong,0,sizeof(belong));
 88     scc_cnt=0;
 89     maxd=0;
 90     for(int i=1;i<=n;i++) G[i].clear();
 91     memset(vis,0,sizeof(vis));
 92 }
 93 
 94 //找到树的直径
 95 void findMaxDeep(int u,int deep)
 96 {
 97     vis[u]=1;
 98 
 99     maxd=max(maxd,deep);
100     for(int i=0;i<G[u].size();i++)
101     {
102         int v = G[u][i];
103         if(!vis[v])
104         {
105             findMaxDeep(v,deep+1);
106         }
107     }
108 }
109 
110 //用bfs来找到一个叶子节点
111 int findRoot()
112 {
113     queue<int> Q;
114     Q.push(1);
115     vis[1]=1;
116     int last=1;
117     while(!Q.empty())
118     {
119         int x = Q.front();Q.pop();
120         for(int i=0;i<G[x].size();i++)
121         {
122             int v = G[x][i];
123             if(!vis[v])
124             {
125                 Q.push(v);
126                 vis[v]=1;
127                 last=v;
128             }
129         }
130     }
131     return last;
132 }
133 
134 void solve()
135 {
136     for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
137 
138     //重新建图
139     for(int i=1;i<=n;i++)
140     {
141         for(int j=head[i];j!=-1;j=E[j].nxt)
142         {
143             int v = E[j].v;
144             int x = belong[i];
145             int y = belong[v];
146             if(x!=y)
147             {
148                 G[x].push_back(y);
149                 G[y].push_back(x);
150             }
151         }
152     }
153 
154     int root=findRoot();
155     memset(vis,0,sizeof(vis));
156 
157     findMaxDeep(root,0);
158 
159     printf("%d\n",bridge-maxd);
160 }
161 
162 int main()
163 {
164     while(scanf("%d%d",&n,&m)==2)
165     {
166         if(n==0 && m==0) break;
167         init();
168 
169         for(int i=1;i<=m;i++)
170         {
171             int u,v;
172             scanf("%d%d",&u,&v);
173             addEdge(u,v);
174         }
175 
176         solve();
177     }
178 }

  但是奇怪的是,重新建图以后找叶子节点的时候,这里用G[x].size()==2不能够实现。讲道理,原理上是没错的,尽管后来发现如果缩点后只有一个点的话是个例外,但是即使排除了这个特殊情况仍然不行,,,这个问题也留着以后探讨吧。。