2011ACM上海邀请赛I题(Imitation)----强连通缩点+DFS+求传递闭包

PS.上海邀请赛唯一一道图论题,当时做的人不多,就没去看。赛后想了下思路,觉得能做,可是一直WA,找了半天bug才发现。所以把那些比较会卡的数据也贴出来了、自己改完bug后的代码复杂度有点高,随机生成了10000组数据等了半天才出来答案,以为可能要TLE了,一交居然过了,而且比我慢的还有,可能数据小,不卡时间吧、、、

 

 

思路:

强连通缩点+DFS+求传递闭包

(1):要去掉自环
(2):要去重边(原图和新图都要去重)
(3):对缩点后的图深搜时要注意(1.要从每个点开始搜;2.每次更新dis后还要继续往下搜)

CODE:

/*AC代码:336ms*/
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#define MAXN 1005//N
#define MAXM 10005//M
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
struct edge
{
	int u,v,next;
	bool ok;//判断这条边是不是在最基础的图上
}E[MAXM],sE[MAXM];
int head[MAXN],ecnt;
int shead[MAXN],secnt;//缩点后重新构图
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index,scc,top,cas,N,M;
int num[MAXN],dis[MAXN],pre[MAXM];
bool Instack[MAXN];
bool map[MAXN][MAXN];
bool vis[MAXN];
int ans_Max,ans_Min;
void Insert(int u,int v)
{
	E[ecnt].u=u;
	E[ecnt].v=v;
	E[ecnt].next=head[u];
	head[u]=ecnt++;
}
void sInsert(int u,int v)//w表示重边数
{
	sE[secnt].u=u;
	sE[secnt].v=v;
	sE[secnt].ok=false;
	sE[secnt].next=shead[u];
	shead[u]=secnt++;
}
void Tarjan(int u)
{
	int i,v;
	Low[u]=DFN[u]=++Index;
	Stack[++top]=u;
	Instack[u]=true;
	for(i=head[u];i!=-1;i=E[i].next)
	{
		v=E[i].v;
		if(!DFN[v])
		{
			Tarjan(v);
			if(Low[u]>Low[v])
				Low[u]=Low[v];
		}
		else if(Instack[v]&&Low[u]>DFN[v])
			Low[u]=DFN[v];
	}
	if(Low[u]==DFN[u])
	{
		scc++;
		do{
			v=Stack[top--];
			Instack[v]=false;
			Belong[v]=scc;
			num[scc]++;
		}while(u!=v);
	}
	return;
}
void Init()
{
	int i,j,u,v;
	scanf("%d%d",&N,&M);
	memset(head,-1,sizeof(head));ecnt=0;//原图
	memset(shead,-1,sizeof(shead));secnt=0;
	memset(map,false,sizeof(map));
	ans_Max=ans_Min=0;
	for(i=1;i<=M;i++)
	{
		scanf("%d%d",&u,&v);
		map[u][v]=true;
	}
	//去重边
	for(i=1;i<=N;i++)
	{
		for(j=1;j<=N;j++)
		{
			if(i!=j&&map[i][j])
				Insert(i,j);
		}
	}
}
void Shrink()//缩点+重新构图
{
	int i,j,u,v;
	memset(num,0,sizeof(num));
	memset(DFN,0,sizeof(DFN));
	memset(Instack,false,sizeof(Instack));
	memset(Low,0,sizeof(Low));
	Index=scc=top=0;
	for(i=1;i<=N;i++)
	{if(!DFN[i]) Tarjan(i);}
	for(i=1;i<=scc;i++)
	{
		if(num[i]!=1)
			ans_Min+=num[i];
	}
	//重新构图
	memset(map,false,sizeof(map));
	for(i=0;i<ecnt;i++)
	{
		u=E[i].u;v=E[i].v;
		if(Belong[u]!=Belong[v])
			map[Belong[u]][Belong[v]]=true;
	}
	for(i=1;i<=scc;i++)
	{
		for(j=1;j<=scc;j++)
		{
			if(j!=i&&map[i][j])
				sInsert(i,j);
		}  
	}
}
void dfs(int u)
{
	int i,v;
	for(i=head[u];i!=-1;i=E[i].next)
	{
		v=E[i].v;
		if(!vis[v])
		{
			vis[v]=1;
			dfs(v);
		} 
	}
}
int Closuer()//在原图上求传递闭包
{
	int i,j,sum=0;
	for(i=1;i<=N;i++)
	{
		memset(vis,false,sizeof(vis));
		vis[i]=true;
		dfs(i);
		for(j=1;j<=N;j++)
		{
			if(vis[j]) sum++;
		}
		sum--;
	}
	return sum;
}
void dfs2(int u,int len)
{
	int i,v;
	for(i=shead[u];i!=-1;i=sE[i].next)
	{
		v=sE[i].v;
		if(dis[v]==-1)
		{
			dis[v]=len;
			pre[v]=i;
			dfs2(v,len+1);
		}
		else
		{
			if(len>dis[v])
			{
				dis[v]=len;
				pre[v]=i;
				dfs2(v,len+1);//注意还要往下搜
			}
		}
	}
}
int Run()//计算缩点后的图中基本的边个数
{
	int i,j,res=0;
	for(i=1;i<=scc;i++)//从每个入度为零的点开始深搜
	{
		memset(dis,-1,sizeof(dis));
		memset(pre,-1,sizeof(pre));
		dis[i]=0;
		dfs2(i,1);
		for(j=1;j<=scc;j++)
		{
			if(pre[j]!=-1)
				sE[pre[j]].ok=true;
		}
	}
	for(i=0;i<secnt;i++)
	{
		if(sE[i].ok)
			res++;
	}
	return res;
}
void Solve()
{
	ans_Max=Closuer();
	Shrink();
	ans_Min+=Run();
	printf("Case #%d: %d %d\n",cas++,ans_Min,ans_Max);
}
int main()
{
	//char s[100];
	//freopen("I.in","r",stdin);
	//freopen("I(2).out","w",stdout);
	int T;
	cas=1;
	scanf("%d",&T);
	while(T--)
	{
		//scanf("%s",s);
		Init();
		Solve();
	}
	return 0;
}
/*
14 20
2 1
9 1
13 14
13 6
7 14
9 10
3 1
14 8
3 9
11 4
7 6
13 11
9 10
12 6
12 2
13 9
6 3
7 5
11 9
8 6
Case #1: 15 48

7 11
1 2
1 3
1 5
1 7
2 3
3 4
4 2
4 5
5 6
6 7
7 5
Case #2: 8 27

18 19
3 4
2 7
16 17
9 8
13 3
3 16
6 13
7 17
3 14
5 4
18 14
3 13
13 2
16 9
14 7
10 5
8 10
18 1
6 18
Case #3: 18 61

11 24
6 6
2 6
5 11
9 7
8 4
11 3
9 3
1 3
2 5
11 7
10 2
2 8
5 5
3 1
7 7
11 7
10 2
6 7
6 3
2 3
8 5
5 4
6 3
7 1
Case #4: 12 41

9 14
4 5
2 9
7 4
3 6
2 7
2 5
3 1
4 9
8 6
3 8
6 4
8 5
7 6
1 2
Case #5: 9 32


9 10
2 7
2 5
3 1
4 9
8 6
3 8
6 4
8 5
7 6
1 2
Case #6: 10 29

9 9
2 5
3 1
4 9
8 6
3 8
6 4
8 5
7 6
1 2
Case #7: 9 20

9 9
1 2
3 1
6 4
9 6
2 4
9 3
9 4
4 8
2 8
Case #8: 7 18
*/


 

 


 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__简言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值