By Recognizing These Guys, We Find Social Networks Us第36届ACM国际大学生程序设计竞赛亚洲区预赛北京邀请赛bupt 197 tarjan+割边

/*
题目地址:http://w.boj.me/onlinejudge/newoj/showProblem/show_problem.php?problem_id=197

题意:给你一个人际关系网(双向的),让你求关键的关系,既如果这条关系断了,就会有某两个或以上的人失去联系了
求这样的关系

解法:tarjan算法 求割边


dfn[v]记录到达点v 的时间,low[v]表示通过它的子结点
可以到达的所有点中时间最小值,即low[i]=min(low[i],low[u]),u 为v 的
了孙,初始化时low[v]=dfn[u]。如果low[v]比dfn[v]小,说明v 可以通过
它的子结点u,u1,u2...到达它的祖先v',则存在环,这个环上所有的点组成的
子图便是一个强连通分量。换一个角度看,如果当low[v]==dfn[v]时,则它的
子树中所有low[u]==dfn[v]的点都与v 构成一个环,维护一个栈,DFS 过程中,
每遍历一个点则把它放入栈中,当发现low[v]==dfn[v]则依次把栈里的元素都
弹出来,当栈顶元素为v 时结束,这些点便构成一个以v 为树根的强连通分量。

分析:
如果low[u]>=dfn[v],说明v 的儿子u 不能通过其他边到达v 的祖先,此
时如果拿掉v,则必定把v 的祖先和v 的儿子u,及它的子孙分开,于是v 便是
一个割点,v 和它的子孙形成一个块。
如果low[u]>dfn[v]时,则说明u 不仅不能到达v 的祖先,连v 也不能通
过另外一条边直接到达,从而它们之间的边w(v,u)便是割边,
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<map>
using namespace std;
//********************************************************************
const int MAX=10009;
const int MAXE=200009;
int n,m;
map<string,int> Map;         //为字符串建立索引,既为每个人编号
map<string,int> T;           //string 为 人名1+" "+人名2 的关系,int为这个关系的出现次序
string ans[10009];           //ans[i]表示第i个人的名字(字符串),使得从编号定位到人
int ansnum;                  //ans的下标

struct cmp					//优先队列的比较,即在关系输入时顺序在前的排前面
{
	bool operator()(const string  s1,const string s2)
	{
		return T[s1]>T[s2];   
	}
};
priority_queue<string,vector<string>,cmp> q;//为关系定义优先队列,为最后按顺序输出准备
//***********************************************************************
struct EDGE    
{    
    int v; // 从u点出发能到达的点v    
    int next; // 从u点出发能到达的下一条边的编号    
}edge[MAXE];  
	
int first[MAX];   // first[u] = e:从点u出发的最后一条边的编号是e(“最后”是指最后输入)    
int dfn[MAX];  // dfn[u]:节点u搜索的次序编号(时间戳)    
int low[MAX];// low[u]:是u或u的子树能够追溯到的最早的栈中节点的次序号    
int endex; // 次序编号 
int Stack[MAX];//栈
int top;//栈顶指针

int DFS(int x,int fa)    //tarjan算法
{    
        low[x]=dfn[x]=endex++;  
		Stack[++top]=x;
        for(int k=first[x];k!=-1;k=edge[k].next)    
        {    
            int v=edge[k].v;   
			if(v==fa)continue;	
            if(dfn[v]==0) //时间戳为0,表示未访问过
            {    
                DFS(v,x); 
				low[x]=min(low[x],low[v]);
				if(dfn[x]<low[v])//若x的子节点v无法追溯到x或x的祖先,那么x-v就是一条割边
				{
					string s=ans[x]+" "+ans[v];
					if(T[s]==0)
					{
						q.push(ans[v]+" "+ans[x]);
					}
					else q.push(s);          //将这条割边代表的关系放入优先队列中
					while(Stack[top]!=x)
					{
					 top--;     //沿栈找回x,途中的边必不是割边,注意x并为出栈
					}
				}
            }    
			else
			{
				low[x]=min(low[x],dfn[v]);//有横向边时,更新最早时间
			}
        }    
        return 1;  
    }    
      
//**************************************************************************
void solve()
{		
		Map.clear();
		T.clear();
		while(!q.empty()) q.pop();
		
        memset(dfn,0,sizeof(dfn));    
        memset(low,0,sizeof(low));    
        memset(first,-1,sizeof(first));      	
		scanf("%d%d",&n,&m);
     
	  
		endex=1;    	
		ansnum=1;
		top=-1;
		
		int a,b;
		int Mnum=1;
        int e=0; 

		string s1,s2;
		int tmp=0;
		for(int i=0;i<m;i++)
		{
			cin>>s1>>s2;
			T[s1+" "+s2]=++tmp;// 为关系建立索引
			if(Map[s1]==0)
			{
				Map[s1]=Mnum++;// 给人名编号
				ans[ansnum++]=s1;//建立编号定位人的机制
			}
			if(Map[s2]==0)
			{
				Map[s2]=Mnum++;
				ans[ansnum++]=s2;
			}
			a=Map[s1];
			b=Map[s2];
			edge[e].v=b;
			edge[e].next=first[a];first[a]=e++;
			
			edge[e].v=a;
			edge[e].next=first[b];first[b]=e++;
		}
		DFS(1,1);
		if(q.empty()||endex<=n||n==1)  cout<<"0"<<endl;//若endex<=n则图不全联通
		else
		{
			cout<<q.size()<<endl;
			while(!q.empty())
			{
				cout<<q.top()<<endl;
				q.pop();	
			}
		}
}
//***********************************************************************
int main()
{
	int Case;
	scanf("%d",&Case);
	while(Case--)
	{
		 solve();
	}
return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值