HDU - 4587 TWO NODES 求割点变形(去掉一个割点能得到的最大连通块数)

题意:给出一张图,问从中任意去掉两个点及其邻接边,所有可能情况剩下的图中连通块的数量最大是多少。

思路:先枚举去电其中一个点,剩下的一个点用tarjan求无向图割点类似的方法求个最大值,具体就是当一个点能成为割点时,我们不是将其标记出来,而是将其计数器+1,最后取一个最大值就行了。

需要注意的就是当根节点为割点时,将其去掉以后得到的新连通块数量是son - 1.(son为搜索树上其儿子的数量)

这个题逼着我重新理解了一遍tarjan算法求无向图割边割点,也算是很有意义了。

代码:

#include
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair P;
const int MAXN = 5010;
int pre[MAXN], cnt;
int dfn[MAXN], low[MAXN], block[MAXN], tid;
int ban;
struct node{
	int v, next;
	node() {}
	node(int _v, int _next) : v(_v), next(_next) {}
}mp[MAXN << 1];
void init()
{
	memset(pre, -1, sizeof(pre));
	cnt = 0;
}
void add(int u, int v)
{
	mp[cnt] = node(v, pre[u]); pre[u] = cnt++;
	mp[cnt] = node(u, pre[v]); pre[v] = cnt++;
}
void dfs(int u, int fa)
{
	int v, son = 0;
	low[u] = dfn[u] = ++tid;
	for(int i = pre[u]; ~i; i = mp[i].next)
	{
		v = mp[i].v;
		if(v == fa || v == ban) continue;
		if(!dfn[v])
		{
			son++;
			dfs(v, u);
			low[u] = min(low[u], low[v]);
			if(dfn[u] <= low[v])
			block[u]++;
		}
		else if(dfn[v] < low[u]) low[u] = dfn[v];
	}
	if(fa == -1)//注意这里cut[u]并非等于son
	block[u] = son - 1;
}
int solve(int n)
{
	int ans = 0, tmp;
	for(ban = 0; ban < n; ban++)
	{
		tmp = tid = 0;
		memset(block, 0, sizeof(block));
		memset(dfn, 0, sizeof(dfn));
		for(int i = 0; i < n; i++)
		{
			if(i == ban || dfn[i]) continue;
			dfs(i, -1);
			tmp++;
		}
		for(int i = 0; i < n; i++)
		if(i != ban)
		ans = max(ans, tmp + block[i]);
	}
	return ans;
}
int main()
{
	int n, m, u, v;
	while(~scanf("%d %d", &n, &m))
	{
		init();
		for(int i = 0; i < m; i++)
		{
			scanf("%d %d", &u, &v);
			add(u, v);
		}
		printf("%d\n", solve(n));
	}
 	return 0;
}



你可能感兴趣的:(====图论====,hdu)