/*
题目地址: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;
}
By Recognizing These Guys, We Find Social Networks Us第36届ACM国际大学生程序设计竞赛亚洲区预赛北京邀请赛bupt 197 tarjan+割边
最新推荐文章于 2012-08-04 21:37:12 发布