【个人专题一】强连通+拓扑排序——Poj_2762

题意:给出点的个数和单向边,构成图。问图中两点x,y是否满足“x可以走到y,或者y可以走到x,or的关系
思路:求出强连通分支,构建新图,如果新图是一个没有中断的”链表”,那么输出yes,否则输出no。判断是否为“链表”,用拓扑排序即可,每次排序的时候判断入度为0的是否只有一个,是则继续排,否则说明不是一个”链表“

#include<iostream>
#include<stack>
using namespace std;

struct Node
{
	int dest;
	struct Node *next;
}node[1005],nMap[1005],*p;

stack<int>Q;

bool Visited[1005],InStack[1005];
int dfn[1005],low[1005],belong[1005];
int n,m,nTime,belongNum;
int nMapInNum[1005];//缩点后新图的入度

void AddNode(int origin,int finish,struct Node tnode[])//添加节点
{
	p=new struct Node;
	p->dest=finish;
	p->next=tnode[origin].next;
	tnode[origin].next=p;
}

void init()//初始化
{
	scanf("%d%d",&n,&m);
	memset(Visited,false,sizeof(Visited));
	memset(InStack,false,sizeof(InStack));
	memset(nMapInNum,0,sizeof(nMapInNum));
	belongNum=nTime=0;
	int i,a,b;
	for(i=1;i<=n;i++)
	{
		node[i].next=NULL;
		nMap[i].next=NULL;
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		AddNode(a,b,node);
	}
	while(!Q.empty())
		Q.pop();
}

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

void Tarjan(int t)
{
	dfn[t]=low[t]=++nTime;
	Q.push(t);
	Visited[t]=InStack[t]=true;
	struct Node *q;
	int x;
	for(q=node[t].next;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])
	{
		int top;
		belongNum++;
		do
		{
			top=Q.top();
			belong[top]=belongNum;//缩点标记
			InStack[top]=false;
			Q.pop();
		}while(top!=t);
	}
}

bool topo()//如果新图是一个链表返回true,否则返回false
{
	int emptyNum,emptySub;//emptyNum用于标记拓扑排序时入度为0的个数,下标(单个为空有效)
	int i,k=belongNum;
	//找出空的个数
	emptyNum=0;
	for(i=1;i<=belongNum;i++)
	{
		if(nMapInNum[i]==0) 
		{
			emptyNum++;
			emptySub=i;
		}
	}
	if(emptyNum>1) return false;
	struct Node *q;
	while(k--)
	{
		emptyNum--;//当前点已经排好,不计入
		//Visited[emptySub]=true;
		int x;
		for(q=nMap[emptySub].next;q!=NULL;q=q->next)
		{
			x=q->dest;
			if(--nMapInNum[x]==0)
			{
				emptyNum++;
				emptySub=x;
			}
		}
		if(emptyNum>1) return false;
	}
	return true;
}

void doit()
{
	int i;
	for(i=1;i<=n;i++)
	{
		if(!Visited[i])
			Tarjan(i);
	}
	//缩点,建立新图,并记录每个点的入度
	struct Node *q;
	int x;
	for(i=1;i<=n;i++)
	{
		for(q=node[i].next;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));//用于拓扑排序记录某点是否已经排好
	if(topo()) printf("Yes\n");
	else printf("No\n");
}

int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		init();
		doit();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值