hdu4635 Strongly connected(tarjan计算强连通通分量+缩点+思想)

Strongly connected

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2714    Accepted Submission(s): 1128


Problem Description
Give a simple directed graph with N nodes and M edges. Please tell me the maximum number of the edges you can add that the graph is still a simple directed graph. Also, after you add these edges, this graph must NOT be strongly connected.
A simple directed graph is a directed graph having no multiple edges or graph loops.
A strongly connected digraph is a directed graph in which it is possible to reach any node starting from any other node by traversing edges in the direction(s) in which they point. 
 

Input
The first line of date is an integer T, which is the number of the text cases.
Then T cases follow, each case starts of two numbers N and M, 1<=N<=100000, 1<=M<=100000, representing the number of nodes and the number of edges, then M lines follow. Each line contains two integers x and y, means that there is a edge from x to y.
 

Output
For each case, you should output the maximum number of the edges you can add.
If the original graph is strongly connected, just output -1.
 

Sample Input
  
  
3 3 3 1 2 2 3 3 1 3 3 1 2 2 3 1 3 6 6 1 2 2 3 3 1 4 5 5 6 6 4
 

Sample Output
  
  
Case 1: -1 Case 2: 1 Case 3: 15
 

Source
 

Recommend
zhuyuanchen520   |   We have carefully selected several similar problems for you:   6022  6021  6020  6019  6018 
 

Statistic |  Submit |  Discuss |  Note

题意大概就是:给你一个DAG图,问最多添加多少条边后此DAG图依然是不是强连通图。

对于这道题而言。我们可以反着思考。如果此图是个强连通图,那么最多能有多少条边。

假设最后所求的DAG图有两个部分X图和Y图.此时X图和Y图肯定各是个强连通图,并且X图和Y图之间只有单向的边。

这样才能使DAG图边数最多

假设X图的顶点数为x,Y图的顶点数为y,则DAG图的顶点数为x+y=n

则有:

  • 如果使X图强连通,边数最多为x*(x-1)(即X图的每个顶点到X图其它每个顶点都有一条单向边)
  • 如果使Y图强连通,边数最多为y*(y-1)
  • X图和Y图之间只有单项的边,则边数最多为x*y(即X图的每个顶点到Y图每个顶点都有一条单向边)
通过以上可以求得边数F(DAG)=x*y+x*(x-1)+y*(y-1)=n*n-n-x*y.即DAG图的边数最多为n*n-n-x*y.
由于n一定,所以本题就可以转化为求x*y的最小值,又因为x+y=n。所以x和y差值越大结果越小。
进而本题转化为查找DAG图中顶点最小的强连通分量。
F(DAG)减去已有的边数m即是所求结果。

计算强连通分量可以使用tarjan算法,在统计过程中对强连通分量进行缩点即可~由于我们要求X图和Y图之间只有单向的边,所以
我们要找的Y图 一定是入度为0或者出度为0的强连通分量,否则 此DAG图为强连通图 。

代码如下:
#include <stdio.h>
#include <vector>
#include <stack>
#include <algorithm>
#include <string.h>
using namespace std;
#define N 100000+10
vector<int>link[N];
bool inStack[N];
int dfn[N];
int low[N];
int fa[N];//通过DAG图中 某个顶点所属的强连通分量序号 
int point[N];//统计缩点后某强连通分量的顶点数 
int in[N];//缩点后某强连通分量的入度 
int out[N];//缩点后某强连通分量的出度 
int time;//时间戳 
int sum;//DAG图中连通分量的个数 
int n,m;
stack<int>s;
void doTarjan(int x)
{
	dfn[x]=low[x]=++time;
	s.push(x);
	inStack[x]=true;
	for(int i=0;i<link[x].size();i++)
	{
		int edge=link[x][i];
		if(!dfn[edge])
		{
			doTarjan(edge);
			low[x]=min(low[x],low[edge]);
		}
		else if(inStack[edge])
		{
			low[x]=min(low[x],dfn[edge]);
		}
	}
	if(dfn[x]==low[x])
	{
		sum++;//强连通分量序号 
		int u;
		do
		{
			u=s.top();s.pop();
			inStack[u]=false;
			fa[u]=sum;//缩点 
		}while(u!=x);
	}
	
}
int solve()
{
	for(int i=1;i<=n;i++)
	{
		if(!dfn[i])
		doTarjan(i);
	}
	//如果DAG图已经是个强连通图  直接返回-1 
	if(sum==1) return -1;
	//计算缩点后的强连通分量的入度、出度和顶点数 
	for(int i=1;i<=n;i++)
	{
		point[fa[i]]++;
		for(int j=0;j<link[i].size();j++)
		{
			if(fa[i]==fa[link[i][j]]) continue;
			in[fa[link[i][j]]]++;
			out[fa[i]]++;
		}
	}
	int res=-1;
	//在这里一定要注意 我们要找的Y图 一定是入度为0或者出度为0的强连通分量
	//否则 此DAG图为强连通图 
	for(int i=1;i<=sum;i++)
	{
		if(in[i]&&out[i]) continue; 
		res=max(res,n*n-n-(n-point[i])*point[i]-m);
	}
	return res;
}
int main()
{
	int t,cnt=0;
	scanf("%d",&t);
	while(t--)
	{
		
		time=sum=0;
		memset(fa,0,sizeof(fa));
		memset(in,0,sizeof(in));
		memset(out,0,sizeof(out));
		memset(dfn,0,sizeof(dfn));
		memset(low,0,sizeof(low));
		memset(link,0,sizeof(link));
		memset(point,0,sizeof(point));
		memset(inStack,false,sizeof(inStack));
		scanf("%d %d",&n,&m);
		for(int i=0;i<m;i++)
		{
			int a,b;
			scanf("%d %d",&a,&b);
			link[a].push_back(b);
		}
		
		printf("Case %d: %d\n",++cnt,solve());
	}
}





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值