/**
* 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;
}
POJ 2117 Electricity (tarjan)删割点后求块数
最新推荐文章于 2021-03-02 10:00:14 发布