【题目链接】

ybt 1525:电力
OpenJudge 百练 2117:Electricity(原英文题)

【题目考点】

1. 图论:割点

【解题思路】

连通块就是连通分量。
首先该题是多组数据问题,注意清空相关变量。输入时边数可能为0,因此需要判断顶点数和边数都为0时,再结束程序。注意输入的顶点编号从0开始,本人习惯从1开始,就将顶点编号都增加1。
输入无向图,该无向图未必是连通的。我们需要先统计出原图连通分量的数量connCnt。尝试以每个顶点为起始顶点调用tarjan,成功调用tarjan进行深搜的次数就是连通分量的数量。
设数组connNum,connNum[u]表示u所在的连通分量在去掉顶点u后会变成几个连通分量。
调用tarjan求割点:
如果顶点u没有邻接点,删掉u后这里会变为0个连通分量,connNum[u]初值设为0。
如果顶点u有邻接点,且不是割点,删掉u后还是有1个连通分量,connNum[u]初值应该设为1。
如果顶点u是割点:

  • 如果u是tarjan调用的起始顶点,即dfs生成树的根结点。将connNum[u]初值设为1,表示去掉u后,u的第一个孩子为根的子树为1个连通分量。当看到u的第2个孩子时会判断u是割点,从这里开始每访问到一个孩子,connNum[u]就增加1。对u的所有孩子访问结束后,connNum[u]为u的孩子数,即子树个数,也就是去掉u后会得到的连通分量的数量。
  • 如果u不是tarjan调用的起始顶点,即不是根结点,那么去掉u后,一定存在一个u的双亲所在的连通分量(u的上方子树),因此将connNum[u]初值设为1。在访问u的孩子v时,如果满足dfn[u] <= low[v],那么去掉顶点u后,以v为根的子树为一个连通分量,connNum[u]计数增加1。

在调用tarjan求出connNum数组以及连通分量数量connCnt后,遍历connNum数组。如果去掉顶点i,则相当于先少1个连通分量,而后增加connNum[i]个连通分量。去掉顶点i后连通分量数量为connCnt-1+connNum[i],求该表达式的最大值即为结果。

【题解代码】

解法1:求割点

#include <bits/stdc++.h>
using namespace std;
#define N 10005
int n, m, connCnt, connNum[N], dfn[N], low[N], ts, root, ans;
vector<int> edge[N];
void tarjan(int u)
{//如果u是根,则先有1个子树是连通分量,从第二个子树开始计数。如果u不是根,则其上方子树算1个连通分量。
	int child = 0;
	connNum[u] = edge[u].empty() ? 0 : 1;//connNum[u]:删掉u后,u所在连通分量变为几个连通分量。如果u没有邻接点,删掉u后就少1个连通分量。 
	dfn[u] = low[u] = ++ts;
	for(int v : edge[u])
	{
		if(dfn[v] == 0)
		{
			tarjan(v);
			low[u] = min(low[u], low[v]);
			if(root == u && ++child > 1 || root != u && dfn[u] <= low[v])
				connNum[u]++;//只要u被判断为割点,则一定存在一个子树在删除u后是一个连通分量。
		}
		else
			low[u] = min(low[u], dfn[v]); 
	}
}
int main()
{
	int u, v;
	while(cin >> n >> m && !(n == 0 && m == 0))
	{
		ans = connCnt = ts = 0;
		for(int i = 1; i <= n; ++i)
			edge[i].clear();
		memset(connNum, 0, sizeof(connNum));
		memset(dfn, 0, sizeof(dfn));
		for(int i = 1; i <= m; ++i)
		{
			cin >> u >> v;
			u++, v++;
			edge[u].push_back(v);
			edge[v].push_back(u);
		}
		for(int i = 1; i <= n; ++i) if(dfn[i] == 0)
		{
			connCnt++;//连通分量计数 
			tarjan(root = i);
		}
		for(int i = 1; i <= n; ++i)
			ans = max(ans, connCnt+connNum[i]-1);
		cout << ans << '\n'; 
	}
	return 0;
}
Logo

集算法之大成!助力oier实现梦想!

更多推荐