hdu4635(差一强联通分量)

题意:给一个有向图,问最多加几条边使图不能缩成一个点,也就是再加一条边图就能缩成一点了。所以我叫它“差一强连通分量”。


思路:缩点,记录每个点包含的结点数,找到缩点后入度或者出度为0,且包含结点最少的那个缩点,将整个图分成两部分,一部分是找出的这个点,另一部分是剩下所有点,容易知道,把两部分各自加边成完全图,整幅图仍然不能缩成一个点,然后在两部分的任意两个点建单向变,整幅图就满足题意了,此时再加一条边就能缩成一个点了。设一部分含x个点,另一部分含y个(x+y=n).

所以最大加边数F = x*(x-1) + y*(y-1) + xy - m

容易知道当x-y取两极的时候F最大,所以前面才要把图分成两部分,一部分包含的点最少。

代码:

#include <stdio.h>
#include <string.h>
#include <vector>

using namespace std;
const int maxn = 100005;
vector <int> G[maxn];
int low[maxn],dfn[maxn],stack[maxn],instack[maxn],color[maxn],mark[maxn];
int in[maxn],out[maxn];
int n, m, cnt, sum, top;

void tarjan(int u)
{
	dfn[u] = low[u] = ++cnt;
	stack[++top] = u;
	instack[u] = true;
	int len = G[u].size();
	int v;
	for (int i = 0; i < len; i++)
	{
		v = G[u][i];
		if (!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u],low[v]);
		}
		else if (instack[v])
			low[u] = min(low[u],dfn[v]);
	}
	if (low[u] == dfn[u])
	{
		++sum;
		int count = 0;
		do
		{
			v = stack[top--];
			instack[v] = false;
			color[v] = sum;
			count++;
		} while (v != u);
		mark[sum] = count;
	}
}

void init()
{
	for (int i = 0; i < maxn; i++)
	{
		G[i].clear();
		in[i] = out[i] = dfn[i] = instack[i] = 0;
	}
	sum = cnt = top = 0;
}

int main()
{
	int T,kase = 0;
	scanf("%d",&T);
	while (T--)
	{
		init();++kase;
		int u,v;
		scanf("%d%d",&n,&m);
		for (int i = 0; i < m; i++)
		{
			scanf("%d%d",&u,&v);
			G[u].push_back(v);
		}
		for (int i = 1; i <= n; i++)
			if (!dfn[i]) tarjan(i);
	//	printf("sum = %d\n",sum);
		if (sum == 1) {printf("Case %d: %d\n",kase,-1);continue;}
		for (int u = 1; u <= n; u++)
		{
			int len = G[u].size();
			for (int i = 0; i < len; i++)
			{
				v = G[u][i];
				if (color[u] != color[v])
				{
					out[color[u]]++;
					in[color[v]]++;
				}
			}
		}
		int minpoint = 0x3f3f3f3f;
		for (int i = 1; i <= sum; i++)
		{
			if (in[i] == 0)
				minpoint = min(minpoint,mark[i]);
			if (out[i] == 0)
				minpoint = min(minpoint,mark[i]);
		}
		int maxpoint = n-minpoint;
		int ans = minpoint*(minpoint-1) + maxpoint*(maxpoint-1) + minpoint*maxpoint - m;
		printf("Case %d: %d\n",kase,ans);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值