【个人专题一】强连通——Hoj_3639(RE)

//未完成
#include<iostream>
#include<stack>
using namespace std;
#define MaxNum 5005
struct Node{
	int dest;
	struct Node *next;
}*node[MaxNum],*nMap[MaxNum],*p;

stack<int>Q;
bool Visited[MaxNum],InStack[MaxNum],nMapMax[MaxNum];
int dfn[MaxNum],low[MaxNum],belong[MaxNum];
int nMapNum[MaxNum],nMapNumOld[MaxNum],nMapInNum[MaxNum];
int nTime,beNum,N,M,testNO;
int nTopoSub;

void AddNode(int st,int end,struct Node *tnode[]){
	p=new struct Node;
	p->dest=end;
	p->next=tnode[st];
	tnode[st]=p;
}

void Init(){//初始化
	scanf("%d%d",&N,&M);
	memset(Visited,false,sizeof(Visited));
	memset(InStack,false,sizeof(InStack));
	memset(nMapInNum,0,sizeof(nMapInNum));//新图入度,拓扑排序用
	memset(nMapMax,false,sizeof(nMapMax));//输出用,判断是否是要输出的连通子集
	memset(nMapNum,0,sizeof(nMapNum));
	while(!Q.empty())
		Q.pop();
	nTime=beNum=testNO=0;
	int i;
	for(i=0;i<N;i++)//清空图
		nMap[i]=node[i]=NULL;
	int a,b;
	for(i=0;i<M;i++){//建立原图
		scanf("%d%d",&a,&b);
		AddNode(a,b,node);
	}
}

int min(int ta,int tb){
	if(ta>tb) return tb;
	return ta;
}

void Tarjan(int t){
	low[t]=dfn[t]=++nTime;
	Q.push(t);
	Visited[t]=InStack[t]=true;
	struct Node *q;
	int x;
	for(q=node[t];q!=NULL;q=q->next){
		x=q->dest;
		if(!Visited[x]){
			Tarjan(x);
			low[t]=min(low[t],low[x]);
		}
		else if(InStack[x])
			low[t]=dfn[x];
	}
	if(dfn[t]==low[t]){//找到一个连通子集
		do{
			x=Q.top();
			belong[x]=beNum;
			InStack[x]=false;
			Q.pop();
			nMapNum[beNum]++;//记录各个连通子集的顶点数
		}while(x!=t);
		beNum++;
	}
}

void Doit(){
	int i;
	for(i=0;i<N;i++){
		if(!Visited[i])
			Tarjan(i);
	}
	int x;
	struct Node *q;
	for(i=0;i<N;i++){//建立新图
		for(q=node[i];q!=NULL;q=q->next){
			x=q->dest;
			if(belong[i]!=belong[x]){
				AddNode(belong[i],belong[x],nMap);
				nMapInNum[belong[x]]++;
			}
		}
	}
	memset(Visited,false,sizeof(Visited));//用于标记是否已经拓扑排序过
	for(i=0;i<beNum;i++)
		nMapNumOld[i]=nMapNum[i];//保留记录子连通图的个数
	int y=beNum;
	while(y--){
		for(i=0;i<beNum;i++){//找到新图中入度为0的点
			if(!Visited[i] && nMapInNum[i]==0){
				nTopoSub=i;
				Visited[i]=true;
				break;
			}
		}
		for(q=nMap[i];q!=NULL;q=q->next){
			x=q->dest;
			nMapInNum[x]--;//入度减1
			nMapNum[x]+=nMapNum[i];//因为具有传递性,拥有的顶点加上支持该点的所有顶点
		}
	}
	//输出
	int sub,max=-1,h=0;
	for(i=0;i<beNum;i++){//计算拥护者最多的
		if(nMapNum[i]>max){
			sub=i;
			max=nMapNum[i];
		}
	}
	for(i=0;i<beNum;i++){
		if(nMapNum[i]==max){
			h+=nMapNumOld[i];//加上该连通子集本身的顶点数
			nMapMax[i]=true;
		}
	}
	printf("Case %d: %d\n",++testNO,nMapNum[sub]-1);
	for(i=0;i<N && h>1;i++){//将拥护者最多的那个强连通子集全部输出
		if(nMapMax[belong[i]]){
			printf("%d ",i);
			h--;
		}
	}
	while(h==1){
		if(nMapMax[belong[i]]){
			printf("%d\n",i);
			h--;
		}
		i++;
	}
}

int main()
{
	int K;
	scanf("%d",&K);
	while(K--){
		Init();
		Doit();
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值