/** * tarjan: 求一个图中删除一个割点后所得到的最大块数。 * 在用tarjan去遍历每个结点的时候,判断割点是如何判断的? * low[v] >= dfn[u] (这里注意不要和求割边混淆) * 求割边的时候,是以low[v] > dfn[u]. 这个其实可以画下图就很好理解了。 * 割点的定义是,删除该结点以及和该结点相关联的边。 * 所以用low[v] >= dfn[u] 需要等号。 因为,即使u的子结点能够通过一个环而返回到u结点。 * 也是可以的,因为在连回u的时候,仍然是u的关联边。所以在判断u是否为割点的时候,是同结点u一同删除的。 * 然而割边的定义是,删除该边使图不连通。所以不能使u的子结点连回u的祖先和u本身! * 所以是low[v] > dfn[u]。 * 现在的问题是如何计算删除该割点后的块的数量。 * 实际上只要在遍历当前结点u的时候。 计数出通过该结点引出的边的数量,使得low[v] >= dfn[u] * (当low[v] = dfn[u]的时候,其实是有两条或者两条以上的边属于当前节点u的关联边,但实际上也只算了一次。) * 还有就是考虑到给的图不是连通图。这个好解决,因为在tarjan遍历的时候,必然是遍历每个连通分量的。 * 扫一遍所有节点,计数就行了。 * 不明白的画图理解即可。 其他具体实现看代码。 */ #include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <string> #include <queue> #include <map> #include <vector> #include <algorithm> #define DEBUG 0 #define INF 0x1fffffff #define MAXS 10005 typedef long long LL; using namespace std; int dfn[MAXS], low[MAXS]; int n, m, ans; vector<int> ver[MAXS]; int DFN; void tarjan(int u, int fa) { int cnt = 0, son = 0; dfn[u] = low[u] = ++ DFN; for(int i = 0; i != ver[u].size(); i ++) { int v = ver[u][i]; if(!dfn[v]) { son ++; tarjan(v, u); low[u] = min(low[u], low[v]); if(low[v] >= dfn[u]) { cnt ++; } } else if(v != fa && dfn[v] < low[u]) low[u] = min(low[u], dfn[v]); } if(fa != -1) cnt ++; if(fa == -1 && son <= 1) cnt = 0; ans = max(cnt, ans); } void init() { DFN = 0; for(int i = 0; i <= n; i ++) { ver[i].clear(); dfn[i] = low[i] = 0; } } int main() { //freopen("e.in", "r", stdin); while(cin >> n >> m, n + m) { init(); for(int i = 1; i <= m; i ++) { int v, u; cin >> v >> u; ver[v].push_back(u); ver[u].push_back(v); } int cnt = 0; ans = 0; if(m == 0) { printf("%d\n", n - 1); continue; } for(int i = 0; i < n; i ++) { if(!dfn[i]) { tarjan(i, -1); cnt ++; } } if(ans) cnt --; cout << ans + cnt << endl; } return 0; }