题目链接:http://poj.org/problem?id=2762
做法:
1、把强连通分量缩点
2、缩点后的图拓扑排序,这里可知拓扑排序的一个作用:判断是不是任意两个点A,B之间都有办法从A到B
注释比较详细,面贴代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
#define Max 1010
int n,m,low[Max],cnt,scnt,ord[Max],n2,id[Max],map[Max][Max];/* 这里的ord就是dfn并且以-1表示没有被访问过*/
/*low[]记录与i连接的节点最先访问的次序*/
struct Node{
int e,val;
}item;
/*val可以记录权值,就连通性,似乎此题没这个值也行*/
vector< vector<Node> >g(Max);/*TMD写成vector<vector<Node>>g(Max);没加空格居然通不过编译*/
stack<int>st;
void Tarjan(int e)
{
int t,i;
int min=low[e]=ord[e]=cnt++;
st.push(e);
for(i=0;i<g[e].size();i++)
{
t=g[e][i].e;
if(ord[t]==-1)Tarjan(t);
if(low[t]<min)min=low[t];
}
if(min<low[e])
{
low[e]=min;
return;
}
/*说明有回边,其实这算是Tarjan的一种非主流的写法了,e已经在栈里,
如min<low[e],就是与e连接的节点在早于e已经被访问过,则必然存在回边*/
/*下面的是在dfn[i]==low[i]时才去执行的,所以当min<low[e]时low[e]被修改,
必不再等于dfn[i],故此处就return*/
do
{
id[t=st.top()]=scnt;/*缩点步骤一:id[i]记录第i个点属于第id[i]个连通分量*/
low[t]=n;/*出栈*/
st.pop();
}while(e!=t);
scnt++;
}
void search(int n)
{
int i;
cnt=scnt=0;
memset(ord,-1,sizeof(ord));
for(i=0;i<n;i++)
if(ord[i]==-1)Tarjan(i);
/*没访问的结点都太监(Tarjan)一遍,防止输入的本身就不是连通图*/
}
void base_vertex()
{
int i,j,t;
search(n);
n2=scnt;
/*缩点操作二:依据id数组重新做一个map*/
memset(map,0,sizeof(map));
for(i=0;i<n;i++)
for(j=0;j<g[i].size();j++)
map[id[i]][id[g[i][j].e]]=1;
}
/*拓扑排序*/
int toposort(int n,int *topo)
{
int d[Max],i,j,k;
/*计算入度存入d[i]*/
for(i=0;i<n;i++)
{
d[i]=0;
for(j=0;j<n;j++)d[i]+=map[j][i];
}
for(k=0;k<n;k++)
{
for(i=0;d[i]&&i<n;i++);/*i就是入度为0的点,靠这个写法挺牛逼的*/
if(i==n)return 0;
d[i]=-1;
for(j=0;j<n;j++)
d[j]-=map[i][j];
topo[k]=i;
}
return 1;
}
int main()
{
int ncase,i,j,u,v,topo[Max];
scanf("%d",&ncase);
while(ncase--)
{
memset(topo,0,sizeof(topo));
memset(map,0,sizeof(map));
scanf("%d%d",&n,&m);
for(i=0;i<=n;i++)
g[i].clear();
for(i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
item.e=v-1;
item.val=1;
g[u-1].push_back(item);
}
base_vertex();
toposort(n2,topo);
int flag=1;
for(i=0;i<n2-1;i++)
{
if(!map[topo[i]][topo[i+1]])
{
flag=0;break;
}
}
if(flag)printf("Yes\n");
else printf("No\n");
}
return 0;
}